├── .gitignore ├── AUTHORS ├── COPYING ├── ChangeLog ├── INSTALL ├── LICENSE-MIT ├── Makefile ├── README.md ├── benchmark ├── gevent+con5.txt ├── gevent+con50.txt ├── gevent.txt ├── geventurl.txt ├── siegerc ├── url.txt ├── version4.1+con5.txt ├── version4.1+con50+jemalloc.txt ├── version4.1+con50.txt ├── version4.1.txt ├── worker_version4.1+con50.txt ├── worker_version4.3.txt └── worker_version4.4.txt ├── client └── client.py ├── example ├── app │ ├── __init__.py │ └── wsgi.py └── static │ └── example.jpg ├── src ├── Makefile ├── Module.mk ├── app │ ├── application.c │ ├── application.h │ ├── ramcloud │ │ ├── CRamCloud.h │ │ ├── RejectRules.h │ │ ├── Status.h │ │ ├── app_ramcloud.c │ │ └── app_ramcloud.h │ ├── static │ │ ├── app_static_file.c │ │ └── app_static_file.h │ ├── wheatredis │ │ ├── hashkit.c │ │ ├── md5.c │ │ ├── redis.c │ │ ├── redis.h │ │ └── redis_config.c │ └── wsgi │ │ ├── app_wsgi.c │ │ ├── app_wsgi.h │ │ ├── wsgiinput.c │ │ └── wsgiwrapper.c ├── array.c ├── array.h ├── build_module_table.py ├── config.c ├── debug.c ├── debug.h ├── dict.c ├── dict.h ├── event.c ├── event.h ├── event_epoll.c ├── event_kqueue.c ├── event_select.c ├── hook.c ├── hook.h ├── list.c ├── list.h ├── log.c ├── memalloc.c ├── memalloc.h ├── mempool.c ├── modules.c ├── net.c ├── net.h ├── networking.c ├── networking.h ├── portable.c ├── portable.h ├── protocol │ ├── http │ │ ├── http_parser.c │ │ ├── http_parser.h │ │ ├── proto_http.c │ │ └── proto_http.h │ ├── memcache │ │ ├── proto_memcache.c │ │ └── proto_memcache.h │ ├── protocol.c │ ├── protocol.h │ ├── redis │ │ ├── proto_redis.c │ │ └── proto_redis.h │ └── str_macro.h ├── register.c ├── setproctitle.c ├── sig.c ├── sig.h ├── slab.c ├── slab.h ├── slice.c ├── slice.h ├── stats.c ├── stats.h ├── test_help.h ├── util.c ├── util.h ├── version.h ├── wheatserver.c ├── wheatserver.h ├── worker │ ├── mbuf.c │ ├── mbuf.h │ ├── worker.c │ ├── worker.h │ ├── worker_async.c │ └── worker_sync.c ├── wstr.c └── wstr.h ├── tests ├── list_conf.conf ├── list_conf1.conf ├── memcache.py ├── redis.conf ├── reload.conf ├── test_async.py ├── test_conf.py ├── test_http.py ├── test_memcache.py ├── test_redis.py ├── test_reload.py ├── test_static_allowed_extension.py └── wheatserver_test.py └── wheatserver.conf /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.pyc 4 | *.plist 5 | 6 | # Libraries 7 | *.lib 8 | *.a 9 | 10 | # Shared objects (inc. Windows DLLs) 11 | *.dll 12 | *.so 13 | *.so.* 14 | *.dylib 15 | 16 | # Executables 17 | *.exe 18 | *.out 19 | *.app 20 | 21 | *.gch 22 | wheatserver 23 | wheatworker 24 | tags 25 | 26 | *.rdb 27 | *.log -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # Names should be added to this file like so: 2 | # Name or Organization 3 | 4 | # Initial version author: 5 | Wang Haomai 6 | 7 | # Contributors: 8 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2013, the Wheatserver Author Wang Haomai. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | * Neither the name of Wheatserver nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | 12 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuyuyu101/wheatserver/25dfb895f1d18ad7b7727aae24f0e65be4708c27/ChangeLog -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | See README.md 2 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright 2 | Igor Sysoev. 3 | 4 | Additional changes are licensed under the same terms as NGINX and 5 | copyright Joyent, Inc. and other Node contributors. All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to 9 | deal in the Software without restriction, including without limitation the 10 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | sell copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Top level makefile, the real shit is at src/Makefile 2 | 3 | default: all 4 | 5 | .DEFAULT: 6 | cd src && $(MAKE) $@ 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Wheatserver(Alpha) 2 | =========== 3 | 4 | Full stack sync/asyc(wait) IO Application Framework, help you build your own 5 | application server. 6 | 7 | You can use Wheatserver to develop web server, application proxy server and 8 | db proxy server etc. You will enjoy using sync programming model to have 9 | great performance under async model. 10 | 11 | Now supporting WSGI, Static file distribute and Redis distribute. 12 | 13 | [中文文档](http://www.wzxue.com/wheatserver-2/) 14 | 15 | Feature 16 | =========== 17 | 18 | * Fast: Full C implemented, and discards any module unnecessary and try to 19 | avoid to copy data. 20 | 21 | * Low memory: Keep low memory and take actions to let multi worker processes 22 | share read-only memory. 23 | 24 | * Pluggable: Worker type, protocol implement and application server all can be 25 | remove and add. You can construct your own worker, application server and 26 | other. 27 | 28 | * Statistic: You can use 'kill -s sigusr1 PID' command or tool to get statistic 29 | information from wheatserver. 30 | 31 | * Reload: If you change config file and apply it without restart wheatserver. 32 | Only you need to do is 'kill -s sighup PID' let wheatserver reload file and 33 | reset workers. 34 | 35 | * Manage: Administrator can get information of running wheatserver by command 36 | interface which can be accessed via client/client.py. You can get more info by 37 | enter 'help' with running it. 38 | 39 | * Module extend: Module can define own statistic information, command(accessed 40 | by Wheatserver client) and configuration in simple way. All module defines can 41 | utilize Wheatserver's admin tools. 42 | 43 | * Simple: Try to hide complexity logical processing and provide simple 44 | interfaces to developer. 45 | 46 | * Implemented: 47 | 48 | > Workers: Sync Worker and Async Worker 49 | 50 | > Protocol: Http 1.0 and Http 1.1, Redis protocol 51 | 52 | > Application Server: WSGI support and static file support both under Http, 53 | > Redis-cluster app support under Redis protocol 54 | 55 | TODO Feature 56 | =========== 57 | 58 | * Change module form and make it separating Wheatserver with module(doing) 59 | * Make Wheatserver statistic component more flexible and add alarm 60 | * Web Dashboard aimed to replace with CLI interactive 61 | * Add SystemTap Toolkit support to monitor the status of application and for 62 | ease of Ops 63 | * Refactor ugly code such as configuration definition and replace with more clear 64 | definition. 65 | * Provide Static Module with `fd`(file description) cache like Nginx 66 | * Provide with multi IP address binding 67 | * Add corotine worker support 68 | 69 | If anyone is interested in Wheatserver and join us, feel free to mail me! 70 | 71 | Build 72 | =========== 73 | 74 | Requestments: python, python-dev 75 | 76 | Support Platform: Linux, Macosx 77 | 78 | Support Web Service: WSGI 79 | 80 | shell > cd wheatserver 81 | 82 | shell > cd src 83 | 84 | shell > make 85 | 86 | Run 87 | =========== 88 | 89 | -./wheatserver --app-project-path {your app path } --app-project-name {app filename} --app-name {callable object} 90 | 91 | Config 92 | =========== 93 | 94 | *Attention*: you must specify needed options under your choosing `protocol` 95 | 96 | See [wheatserver.conf](https://github.com/yuyuyu101/wheatserver/blob/master/wheatserver.conf) 97 | 98 | WSGI Example(Sample) 99 | =========== 100 | 101 |
102 | #sample.py which is in the wheatserver/src
103 | HELLO_WORLD = b"Hello world!\n"
104 | 
105 | def simple_app(environ, start_response):
106 |     """Simplest possible application object"""
107 |     status = '200 OK'
108 |     response_headers = [('Content-type', 'text/plain')]
109 |     start_response(status, response_headers)
110 |     return [HELLO_WORLD]
111 | 
112 | 113 | -./wheatserver --app-project-name sample --app-name simple_app 114 | 115 | WSGI Example(Django) 116 | =========== 117 | 118 | -My Django Project Directory: 119 |
120 | |-signup
121 |    |-wsgi.py
122 |    |-bin
123 |    |-include
124 |    |-lib
125 |    |-signup
126 |    |---activity
127 |    |-----fixtures
128 |    |-----static
129 |    |-------css
130 |    |-------img
131 |    |-------js
132 |    |---assets
133 |    |-----static
134 |    |---benefits
135 |    |-----templatetags
136 |    |---finance
137 |    |-----templatetags
138 |    |---fixtures
139 |    |---logs
140 |    |---match
141 |    |-----......
142 |    |---media
143 |    |-----......
144 |    |---snapboard
145 |    |-----.......
146 |    |---specialist
147 |    |-----static
148 |    |-------css
149 |    |-------js
150 |    |-----templates
151 |    |-------admin
152 |    |---------specialist
153 |    |---static
154 |    |---templates
155 |    |-----......
156 |    |---third_user
157 |    |-----static
158 |    |---settings.py
159 |    |---urls.py
160 | 
161 | 162 | wsgi.py at the top of tree is the entry of Django WSGI. 163 | 164 |
165 | import os, sys
166 | 
167 | sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'signup'))
168 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "signup.settings")
169 | 
170 | # This application object is used by the development server
171 | # as well as any WSGI server configured to use this file.
172 | from django.core.wsgi import get_wsgi_application
173 | application = get_wsgi_application()
174 | 
175 | 176 | -shell> ./wheatserver --app-project-path /Users/abcd/signup/ --app-module-name wsgi --app-name application 177 | 178 | Admin management 179 | =========== 180 | 181 | shell > client/client.py 182 | 183 | enter `help` to get command list and description 184 | 185 | Signals for controlling 186 | =========== 187 | 188 |
189 | # using reload to send the signal
190 | kill -HUP `cat /tmp/wheatserver.pid`
191 | # using kill to send the signal
192 | kill -INT `cat /tmp/project-master.pid`
193 | 
194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 |
SignalDescription
SIGHUPreload configuration file, gracefully reload all the workers and the master process
SIGTERMbrutally kill all the workers and the master process
SIGINTbrutally kill the workers and the master process
SIGQUITgracefully kill the workers and the master process
SIGUSR1print statistics
SIGUSR2reexec the entire master process and spawn workers
SIGTTINadd one to workernumber
SIGTTOUsubtraction of one from workernumber
SIGWINCHonly gracefully kill the workers and the master process backend
237 | -------------------------------------------------------------------------------- /benchmark/gevent+con5.txt: -------------------------------------------------------------------------------- 1 | ** SIEGE 2.74 2 | ** Preparing 5 concurrent users for battle. 3 | The server is now under siege... done. 4 | 5 | Transactions: 500 hits 6 | Availability: 100.00 % 7 | Elapsed time: 0.31 secs 8 | Data transferred: 40.42 MB 9 | Response time: 0.00 secs 10 | Transaction rate: 1612.90 trans/sec 11 | Throughput: 130.39 MB/sec 12 | Concurrency: 4.90 13 | Successful transactions: 500 14 | Failed transactions: 0 15 | Longest transaction: 0.01 16 | Shortest transaction: 0.00 17 | 18 | -------------------------------------------------------------------------------- /benchmark/gevent+con50.txt: -------------------------------------------------------------------------------- 1 | ** SIEGE 2.74 2 | ** Preparing 50 concurrent users for battle. 3 | The server is now under siege... done. 4 | 5 | Transactions: 5000 hits 6 | Availability: 100.00 % 7 | Elapsed time: 4.11 secs 8 | Data transferred: 937.78 MB 9 | Response time: 0.04 secs 10 | Transaction rate: 1216.54 trans/sec 11 | Throughput: 228.17 MB/sec 12 | Concurrency: 49.07 13 | Successful transactions: 5000 14 | Failed transactions: 0 15 | Longest transaction: 0.10 16 | Shortest transaction: 0.00 17 | 18 | -------------------------------------------------------------------------------- /benchmark/gevent.txt: -------------------------------------------------------------------------------- 1 | ** SIEGE 2.74 2 | ** Preparing 50 concurrent users for battle. 3 | The server is now under siege... done. 4 | 5 | Transactions: 5000 hits 6 | Availability: 100.00 % 7 | Elapsed time: 2.95 secs 8 | Data transferred: 397.76 MB 9 | Response time: 0.03 secs 10 | Transaction rate: 1694.92 trans/sec 11 | Throughput: 134.83 MB/sec 12 | Concurrency: 49.18 13 | Successful transactions: 5000 14 | Failed transactions: 0 15 | Longest transaction: 0.08 16 | Shortest transaction: 0.00 17 | 18 | -------------------------------------------------------------------------------- /benchmark/geventurl.txt: -------------------------------------------------------------------------------- 1 | http://127.0.0.1:8088/ 2 | http://127.0.0.1:8088/complex 3 | -------------------------------------------------------------------------------- /benchmark/siegerc: -------------------------------------------------------------------------------- 1 | verbose = false 2 | concurrent = 50 3 | reps = 100 4 | delay = 0 5 | internet = true 6 | benchmark = true 7 | -------------------------------------------------------------------------------- /benchmark/url.txt: -------------------------------------------------------------------------------- 1 | http://127.0.0.1:10828/ 2 | http://127.0.0.1:10828/complex 3 | -------------------------------------------------------------------------------- /benchmark/version4.1+con5.txt: -------------------------------------------------------------------------------- 1 | ** SIEGE 2.74 2 | ** Preparing 5 concurrent users for battle. 3 | The server is now under siege... done. 4 | 5 | Transactions: 500 hits 6 | Availability: 100.00 % 7 | Elapsed time: 0.26 secs 8 | Data transferred: 95.22 MB 9 | Response time: 0.00 secs 10 | Transaction rate: 1923.08 trans/sec 11 | Throughput: 366.22 MB/sec 12 | Concurrency: 4.92 13 | Successful transactions: 500 14 | Failed transactions: 0 15 | Longest transaction: 0.01 16 | Shortest transaction: 0.00 17 | 18 | -------------------------------------------------------------------------------- /benchmark/version4.1+con50+jemalloc.txt: -------------------------------------------------------------------------------- 1 | ** SIEGE 2.74 2 | ** Preparing 50 concurrent users for battle. 3 | The server is now under siege... done. 4 | 5 | Transactions: 5000 hits 6 | Availability: 100.00 % 7 | Elapsed time: 2.69 secs 8 | Data transferred: 920.18 MB 9 | Response time: 0.03 secs 10 | Transaction rate: 1858.74 trans/sec 11 | Throughput: 342.07 MB/sec 12 | Concurrency: 48.64 13 | Successful transactions: 5000 14 | Failed transactions: 0 15 | Longest transaction: 0.26 16 | Shortest transaction: 0.00 17 | 18 | -------------------------------------------------------------------------------- /benchmark/version4.1+con50.txt: -------------------------------------------------------------------------------- 1 | ** SIEGE 2.74 2 | ** Preparing 50 concurrent users for battle. 3 | The server is now under siege... done. 4 | 5 | Transactions: 5000 hits 6 | Availability: 100.00 % 7 | Elapsed time: 2.58 secs 8 | Data transferred: 931.09 MB 9 | Response time: 0.02 secs 10 | Transaction rate: 1937.98 trans/sec 11 | Throughput: 360.89 MB/sec 12 | Concurrency: 45.95 13 | Successful transactions: 5000 14 | Failed transactions: 0 15 | Longest transaction: 0.07 16 | Shortest transaction: 0.00 17 | 18 | -------------------------------------------------------------------------------- /benchmark/version4.1.txt: -------------------------------------------------------------------------------- 1 | ** SIEGE 2.74 2 | ** Preparing 50 concurrent users for battle. 3 | The server is now under siege... done. 4 | 5 | Transactions: 5000 hits 6 | Availability: 100.00 % 7 | Elapsed time: 9.50 secs 8 | Data transferred: 7017.57 MB 9 | Response time: 0.09 secs 10 | Transaction rate: 526.32 trans/sec 11 | Throughput: 738.69 MB/sec 12 | Concurrency: 47.83 13 | Successful transactions: 5000 14 | Failed transactions: 0 15 | Longest transaction: 0.35 16 | Shortest transaction: 0.00 17 | 18 | -------------------------------------------------------------------------------- /benchmark/worker_version4.1+con50.txt: -------------------------------------------------------------------------------- 1 | ** SIEGE 2.74 2 | ** Preparing 50 concurrent users for battle. 3 | The server is now under siege... done. 4 | 5 | Transactions: 5000 hits 6 | Availability: 100.00 % 7 | Elapsed time: 2.92 secs 8 | Data transferred: 922.91 MB 9 | Response time: 0.03 secs 10 | Transaction rate: 1712.33 trans/sec 11 | Throughput: 316.06 MB/sec 12 | Concurrency: 49.42 13 | Successful transactions: 5000 14 | Failed transactions: 0 15 | Longest transaction: 0.21 16 | Shortest transaction: 0.01 17 | 18 | -------------------------------------------------------------------------------- /benchmark/worker_version4.3.txt: -------------------------------------------------------------------------------- 1 | ** SIEGE 2.74 2 | ** Preparing 50 concurrent users for battle. 3 | The server is now under siege... 4 | Lifting the server siege... done. 5 | 6 | Transactions: 3747 hits 7 | Availability: 100.00 % 8 | Elapsed time: 1.95 secs 9 | Data transferred: 699.99 MB 10 | Response time: 0.03 secs 11 | Transaction rate: 1921.54 trans/sec 12 | Throughput: 358.97 MB/sec 13 | Concurrency: 48.88 14 | Successful transactions: 3747 15 | Failed transactions: 0 16 | Longest transaction: 0.08 17 | Shortest transaction: 0.00 18 | 19 | -------------------------------------------------------------------------------- /benchmark/worker_version4.4.txt: -------------------------------------------------------------------------------- 1 | ** SIEGE 2.74 2 | ** Preparing 50 concurrent users for battle. 3 | The server is now under siege... done. 4 | 5 | Transactions: 5000 hits 6 | Availability: 100.00 % 7 | Elapsed time: 2.53 secs 8 | Data transferred: 915.22 MB 9 | Response time: 0.02 secs 10 | Transaction rate: 1976.28 trans/sec 11 | Throughput: 361.75 MB/sec 12 | Concurrency: 48.79 13 | Successful transactions: 5000 14 | Failed transactions: 0 15 | Longest transaction: 0.08 16 | Shortest transaction: 0.00 17 | 18 | -------------------------------------------------------------------------------- /client/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import code 4 | import sys 5 | import socket 6 | 7 | def server_socket(port): 8 | s = socket.socket() 9 | s.connect(("127.0.0.1", port)) 10 | return s 11 | 12 | def construct_command(*args): 13 | return "\r\r%s$" % ("\n".join(args)) 14 | 15 | if __name__ == '__main__': 16 | print "Wheatserver admin client. Usage: ./client.py [port]" 17 | if len(sys.argv) > 1: 18 | port = sys.argv[1] 19 | else: 20 | port = 10829 21 | try: 22 | s = server_socket(port) 23 | except socket.error: 24 | print "Connect to port %d failed" % port 25 | else: 26 | s.settimeout(0.1) 27 | while 1: 28 | try: 29 | r = code.InteractiveConsole(locals=globals()).raw_input(">>> ") 30 | command = construct_command(*r.split()) 31 | s.send(command) 32 | try: 33 | a = s.recv(100) 34 | while a: 35 | print a, 36 | a = s.recv(100) 37 | except socket.timeout: 38 | pass 39 | except EOFError, KeyboardInterrupt: 40 | break 41 | print "exit" 42 | -------------------------------------------------------------------------------- /example/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuyuyu101/wheatserver/25dfb895f1d18ad7b7727aae24f0e65be4708c27/example/app/__init__.py -------------------------------------------------------------------------------- /example/app/wsgi.py: -------------------------------------------------------------------------------- 1 | HELLO_WORLD = b"Hello world!\n" 2 | COMPLEX = b"Hello world!\n" * 20000 3 | 4 | def application(environ, start_response): 5 | """Simplest possible application object""" 6 | status = '200 OK' 7 | post = environ['wsgi.input'].read(1000) 8 | ret = HELLO_WORLD 9 | if environ['PATH_INFO'] == '/complex': 10 | ret = COMPLEX 11 | elif environ['PATH_INFO'] == '/file': 12 | ret = open('example/static/example.jpg') 13 | response_headers = [('Content-type', 'img/jpg')] 14 | start_response(status, response_headers) 15 | return ret 16 | elif post: 17 | ret = post 18 | response_headers = [('Content-type', 'text/plain'), ('Content-Length', str(len(ret)))] 19 | start_response(status, response_headers) 20 | 21 | return [ret] 22 | 23 | if __name__ == '__main__': 24 | from gevent.pywsgi import WSGIServer 25 | print 'Serving on 8088...' 26 | WSGIServer(('', 8088), application).serve_forever() 27 | -------------------------------------------------------------------------------- /example/static/example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuyuyu101/wheatserver/25dfb895f1d18ad7b7727aae24f0e65be4708c27/example/static/example.jpg -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CC=cc 2 | 3 | CORE_SOURCES = config.c net.c log.c wstr.c list.c dict.c hook.c sig.c \ 4 | networking.c util.c register.c stats.c event.c setproctitle.c \ 5 | slice.c debug.c portable.c memalloc.c array.c \ 6 | app/application.c protocol/protocol.c worker/mbuf.c \ 7 | worker/worker.c modules.c 8 | 9 | include Module.mk 10 | 11 | SOURCES += $(CORE_SOURCES) 12 | SOURCES += $(MODULE_SOURCES) 13 | SOURCES += $(REAL_EXPORTS) 14 | 15 | OBJECTS = $(SOURCES:.c=.o) 16 | SERVER_OBJECTS += $(OBJECTS) 17 | SERVER_OBJECTS += wheatserver.o 18 | WORKER_OBJECTS += $(OBJECTS) 19 | WORKER_OBJECTS += wheatworker.o 20 | DEBUG ?= 0 21 | ifeq ($(DEBUG),1) 22 | CFLAGS += -O0 -Wall -g $(EXTRA) 23 | else 24 | CFLAGS += -O3 -Wall $(EXTRA) 25 | endif 26 | 27 | TESTS = test_wstr test_list test_dict test_slice test_mbuf test_array 28 | 29 | all: build_module_table wheatserver wheatworker 30 | 31 | wheatworker.o: wheatserver.c wheatserver.h 32 | $(CC) $(CFLAGS) -c $< -o $@ -DWHEAT_DEBUG_WORKER 33 | 34 | .c.o: 35 | $(CC) $(CFLAGS) -c $< -o $@ 36 | ################ 37 | wheatserver: $(SERVER_OBJECTS) 38 | $(CC) $(CFLAGS) -o wheatserver $(SERVER_OBJECTS) $(LIBS) 39 | 40 | wheatworker: $(WORKER_OBJECTS) 41 | $(CC) $(CFLAGS) -o wheatworker $(WORKER_OBJECTS) $(LIBS) 42 | 43 | build_module_table: 44 | python build_module_table.py "$(MODULE_ATTRS)" 45 | 46 | ########### 47 | test: $(TESTS) 48 | for t in $(TESTS); do echo "***** Running $$t"; ./$$t ; rm $$t || exit 1; done 49 | 50 | test_wstr: wstr.c wstr.h 51 | $(CC) -o $@ wstr.c memalloc.o -DWSTR_TEST_MAIN 52 | ./test_wstr 53 | 54 | test_list: list.c list.h 55 | $(CC) -o $@ list.c memalloc.o -DLIST_TEST_MAIN 56 | ./test_list 57 | 58 | test_dict: dict.c dict.h 59 | $(CC) -o $@ dict.c wstr.c memalloc.o -DDICT_TEST_MAIN 60 | ./test_dict 61 | 62 | test_slice: slice.c slice.h 63 | $(CC) -o $@ slice.c memalloc.c -DSLICE_TEST_MAIN 64 | ./test_slice 65 | 66 | test_array: array.c array.h 67 | $(CC) -o $@ array.c memalloc.c -DARRAY_TEST_MAIN 68 | ./test_array 69 | 70 | test_mbuf: worker/mbuf.c worker/mbuf.h slice.c slice.h 71 | $(CC) -o $@ worker/mbuf.c slice.c memalloc.c -DMBUF_TEST_MAIN 72 | ./test_mbuf 73 | 74 | .PHONY: clean 75 | clean: 76 | rm $(SERVER_OBJECTS) *.gch wheatserver wheatworker wheatworker.o 77 | -------------------------------------------------------------------------------- /src/Module.mk: -------------------------------------------------------------------------------- 1 | WSGI_APP_MODULE = app/wsgi/app_wsgi.c app/wsgi/wsgiwrapper.c app/wsgi/wsgiinput.c 2 | PYTHON_VERSION = $(shell python -c "import distutils.sysconfig;print distutils.sysconfig.get_python_version()") 3 | MODULE_ATTRS += AppWsgiAttr 4 | 5 | CFLAGS += -I$(shell python -c "import distutils.sysconfig;print distutils.sysconfig.get_python_inc()") 6 | LIBS += -lpython$(PYTHON_VERSION) -lramcloud 7 | MODULE_SOURCES += $(WSGI_APP_MODULE) 8 | 9 | ################################ Module Separtor ############################### 10 | STATIC_APP_MODULE = app/static/app_static_file.c 11 | 12 | MODULE_SOURCES += $(STATIC_APP_MODULE) 13 | MODULE_ATTRS += AppStaticAttr 14 | 15 | ################################ Module Separtor ############################### 16 | REDIS_APP_MODULE = app/wheatredis/redis.c app/wheatredis/hashkit.c \ 17 | app/wheatredis/md5.c app/wheatredis/redis_config.c 18 | 19 | MODULE_SOURCES += $(REDIS_APP_MODULE) 20 | MODULE_ATTRS += AppRedisAttr 21 | 22 | ################################ Module Separtor ############################### 23 | RAMCLOUD_APP_MODULE = app/ramcloud/app_ramcloud.c 24 | 25 | MODULE_SOURCES += $(RAMCLOUD_APP_MODULE) 26 | MODULE_ATTRS += AppRamcloudAttr 27 | 28 | ################################ Module Separtor ############################### 29 | HTTP_PROTOCOL_MODULE = protocol/http/http_parser.c protocol/http/proto_http.c 30 | 31 | MODULE_SOURCES += $(HTTP_PROTOCOL_MODULE) 32 | MODULE_ATTRS += ProtocolHttpAttr 33 | 34 | ################################ Module Separtor ############################### 35 | MEMCACHE_PROTOCOL_MODULE = protocol/memcache/proto_memcache.c 36 | 37 | MODULE_SOURCES += $(MEMCACHE_PROTOCOL_MODULE) 38 | MODULE_ATTRS += ProtocolMemcacheAttr 39 | 40 | ################################ Module Separtor ############################### 41 | REDIS_PROTOCOL_MODULE = protocol/redis/proto_redis.c 42 | 43 | MODULE_SOURCES += $(REDIS_PROTOCOL_MODULE) 44 | MODULE_ATTRS += ProtocolRedisAttr 45 | 46 | ################################ Module Separtor ############################### 47 | SYNC_WORKER_MODULE = worker/worker_sync.c 48 | 49 | MODULE_SOURCES += $(SYNC_WORKER_MODULE) 50 | MODULE_ATTRS += SyncWorkerAttr 51 | 52 | ################################ Module Separtor ############################### 53 | ASYNC_WORKER_MODULE = worker/worker_async.c 54 | 55 | MODULE_SOURCES += $(ASYNC_WORKER_MODULE) 56 | MODULE_ATTRS += AsyncWorkerAttr 57 | -------------------------------------------------------------------------------- /src/app/application.c: -------------------------------------------------------------------------------- 1 | // Application common utils 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include "application.h" 8 | 9 | int initApp(struct app *app) 10 | { 11 | if (app->is_init) 12 | return WHEAT_OK; 13 | if (app->initApp(WorkerProcess->protocol) == WHEAT_WRONG) 14 | return WHEAT_WRONG; 15 | app->is_init = 1; 16 | return WHEAT_OK; 17 | } 18 | 19 | int initAppData(struct conn *c) 20 | { 21 | if (c->app && c->app->initAppData) { 22 | c->app_private_data = c->app->initAppData(c); 23 | if (!c->app_private_data) 24 | return WHEAT_WRONG; 25 | } 26 | return WHEAT_OK; 27 | } 28 | -------------------------------------------------------------------------------- /src/app/application.h: -------------------------------------------------------------------------------- 1 | // Application common utils 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_APPLICANT_H 8 | #define WHEATSERVER_APPLICANT_H 9 | 10 | #include "../worker/worker.h" 11 | #include "../wheatserver.h" 12 | 13 | int initAppData(struct conn *); 14 | int initApp(struct app *); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/app/ramcloud/CRamCloud.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010-2013 Stanford University 2 | * 3 | * Permission to use, copy, modify, and distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES 8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR 10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | /** 17 | * \file 18 | * This file defines RAMCloud features available from C rather than C++. 19 | */ 20 | 21 | #ifndef RAMCLOUD_CRAMCLOUD_H 22 | #define RAMCLOUD_CRAMCLOUD_H 23 | 24 | #include 25 | 26 | #include "RejectRules.h" 27 | #include "Status.h" 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | using namespace RAMCloud; 32 | #else 33 | /// Forward Declarations 34 | struct RamCloud; 35 | struct rc_client; 36 | #endif 37 | 38 | typedef enum MultiOp { 39 | MULTI_OP_INCREMENT = 0, 40 | MULTI_OP_READ, 41 | MULTI_OP_WRITE, 42 | MULTI_OP_REMOVE 43 | } MultiOp; 44 | 45 | Status rc_connect(const char* serverLocator, 46 | const char* clusterName, 47 | struct rc_client** newClient); 48 | Status rc_connectWithClient( 49 | struct RamCloud* existingClient, 50 | struct rc_client** newClient); 51 | void rc_disconnect(struct rc_client* client); 52 | 53 | Status rc_createTable(struct rc_client* client, 54 | const char* name, 55 | uint32_t serverSpan); 56 | Status rc_dropTable(struct rc_client* client, const char* name); 57 | 58 | void rc_enumerateTablePrepare(struct rc_client* client, 59 | uint64_t tableId, 60 | int keysOnly, 61 | void **enumerationState); 62 | Status rc_enumerateTableNext(struct rc_client* client, 63 | void *enumerationState, 64 | uint32_t* keyLength, const void** key, 65 | uint32_t* dataLength, const void** data); 66 | void rc_enumerateTableFinalize(void *enumerationState); 67 | 68 | 69 | Status rc_getStatus(struct rc_client* client); 70 | Status rc_getTableId(struct rc_client* client, const char* name, 71 | uint64_t* tableId); 72 | 73 | Status rc_incrementInt64(struct rc_client* client, uint64_t tableId, 74 | const void* key, uint16_t keyLength, 75 | int64_t incrementValue, 76 | const struct RejectRules* rejectRules, 77 | uint64_t* version, int64_t *newValue); 78 | Status rc_incrementDouble(struct rc_client* client, uint64_t tableId, 79 | const void* key, uint16_t keyLength, 80 | double incrementValue, 81 | const struct RejectRules* rejectRules, 82 | uint64_t* version, double *newValue); 83 | Status rc_read(struct rc_client* client, uint64_t tableId, 84 | const void* key, uint16_t keyLength, 85 | const struct RejectRules* rejectRules, 86 | uint64_t* version, void* buf, uint32_t maxLength, 87 | uint32_t* actualLength); 88 | Status rc_remove(struct rc_client* client, uint64_t tableId, 89 | const void* key, uint16_t keyLength, 90 | const struct RejectRules* rejectRules, 91 | uint64_t* version); 92 | Status rc_write(struct rc_client* client, uint64_t tableId, 93 | const void* key, uint16_t keyLength, 94 | const void* buf, uint32_t length, 95 | const struct RejectRules* rejectRules, 96 | uint64_t* version); 97 | 98 | void rc_multiIncrementCreate(uint64_t tableId, 99 | const void *key, uint16_t keyLength, 100 | int64_t incrementInt64, 101 | double incrementDouble, 102 | const struct RejectRules* rejectRules, 103 | void *where); 104 | void rc_multiReadCreate(uint64_t tableId, 105 | const void *key, uint16_t keyLength, 106 | void* buf, uint32_t maxLength, 107 | uint32_t* actualLength, 108 | void *where); 109 | void rc_multiWriteCreate(uint64_t tableId, 110 | const void *key, uint16_t keyLength, 111 | const void* buf, uint32_t length, 112 | const struct RejectRules* rejectRules, 113 | void *where); 114 | void rc_multiRemoveCreate(uint64_t tableId, 115 | const void *key, uint16_t keyLength, 116 | const struct RejectRules* rejectRules, 117 | void *where); 118 | 119 | uint16_t rc_multiOpSizeOf(MultiOp type); 120 | Status rc_multiOpStatus(const void *multiOpObject, MultiOp type); 121 | uint64_t rc_multiOpVersion(const void *multiOpObject, MultiOp type); 122 | void rc_multiOpDestroy(void *multiOpObject, MultiOp type); 123 | 124 | void rc_multiIncrement(struct rc_client* client, 125 | void **requests, uint32_t numRequests); 126 | void rc_multiRead(struct rc_client* client, 127 | void **requests, uint32_t numRequests); 128 | void rc_multiWrite(struct rc_client* client, 129 | void **requests, uint32_t numRequests); 130 | void rc_multiRemove(struct rc_client* client, 131 | void **requests, uint32_t numRequests); 132 | 133 | Status rc_testing_kill(struct rc_client* client, uint64_t tableId, 134 | const void* key, uint16_t keyLength); 135 | Status rc_testing_get_server_id(struct rc_client* client, 136 | uint64_t tableId, 137 | const void* key, 138 | uint16_t keyLength, 139 | uint64_t* serverId); 140 | Status rc_testing_get_service_locator(struct rc_client* client, 141 | uint64_t tableId, 142 | const void* key, 143 | uint16_t keyLength, 144 | char* locatorBuffer, 145 | size_t bufferLength); 146 | Status rc_testing_fill(struct rc_client* client, uint64_t tableId, 147 | const void* key, uint16_t keyLength, 148 | uint32_t objectCount, uint32_t objectSize); 149 | Status rc_set_runtime_option(struct rc_client* client, 150 | const char* option, 151 | const char* value); 152 | void rc_testing_wait_for_all_tablets_normal(struct rc_client* client, 153 | uint64_t timeoutNs); 154 | void rc_set_log_file(const char* path); 155 | 156 | #ifdef __cplusplus 157 | } 158 | #endif 159 | 160 | #endif // RAMCLOUD_CRAMCLOUD_H 161 | -------------------------------------------------------------------------------- /src/app/ramcloud/RejectRules.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Stanford University 2 | * 3 | * Permission to use, copy, modify, and distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES 8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR 10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef RAMCLOUD_REJECTRULES_H 17 | #define RAMCLOUD_REJECTRULES_H 18 | 19 | #include 20 | 21 | #ifdef __cplusplus 22 | namespace RAMCloud { 23 | #endif 24 | 25 | /** 26 | * Used in conditional operations to specify conditions under 27 | * which an operation should be aborted with an error. 28 | * 29 | * RejectRules are typically used to ensure consistency of updates; 30 | * for example, we might want to update a value but only if it hasn't 31 | * changed since the last time we read it. If a RejectRules object 32 | * is passed to an operation, the operation will be aborted if any 33 | * of the following conditions are satisfied: 34 | * - doesntExist is nonzero and the object does not exist 35 | * - exists is nonzero and the object does exist 36 | * - versionLeGiven is nonzero and the object exists with a version 37 | * less than or equal to givenVersion. 38 | * - versionNeGiven is nonzero and the object exists with a version 39 | * different from givenVersion. 40 | */ 41 | struct RejectRules { 42 | uint64_t givenVersion; 43 | uint8_t doesntExist; 44 | uint8_t exists; 45 | uint8_t versionLeGiven; 46 | uint8_t versionNeGiven; 47 | } __attribute__((packed)); 48 | 49 | #ifdef __cplusplus 50 | } // namespace RAMCloud 51 | #endif 52 | 53 | #endif // RAMCLOUD_REJECTRULES_H 54 | 55 | -------------------------------------------------------------------------------- /src/app/ramcloud/Status.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010-2014 Stanford University 2 | * 3 | * Permission to use, copy, modify, and distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES 8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR 10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | /** 17 | * \file 18 | * This file defines the status values (error codes) returned to 19 | * clients by RAMCloud operations. 20 | */ 21 | 22 | #ifndef RAMCLOUD_STATUS_H 23 | #define RAMCLOUD_STATUS_H 24 | 25 | #ifdef __cplusplus 26 | namespace RAMCloud { 27 | #endif 28 | 29 | /** 30 | * This enum provides symbolic names for the status values returned 31 | * to applications by RAMCloud operations. 32 | * 33 | * 0 means success; anything else means that an error occurred. 34 | * Not all status values can be returned by all operations. 35 | */ 36 | typedef enum Status { 37 | /// Default return value when an operation was successful. 38 | STATUS_OK = 0, 39 | 40 | /// Indicates that the server does not know about (and is not responsible 41 | /// for) a given table, but that it may exist elsewhere in the system. 42 | /// When it's possible that the table exists on another server, this status 43 | /// should be returned (in preference to the definitive TABLE_DOESNT_EXIST). 44 | STATUS_UNKNOWN_TABLET = 1, 45 | 46 | /// Indicates that a table does not exist anywhere in the system. At present 47 | /// only the coordinator can say with certainly that a table does not exist. 48 | STATUS_TABLE_DOESNT_EXIST = 2, 49 | 50 | 51 | /// Indicates that an object does not exist anywhere in the system. Note 52 | /// that unlike with tables there is no UNKNOWN_OBJECT status. This is just 53 | /// because servers will reject operations on objects in unknown tables with 54 | /// a table-related status. If they own a particular tablet, then they can 55 | /// say with certainty if an object exists there or not. 56 | STATUS_OBJECT_DOESNT_EXIST = 3, 57 | 58 | // TODO(anyone) More documentation below, please. 59 | 60 | STATUS_OBJECT_EXISTS = 4, 61 | STATUS_WRONG_VERSION = 5, 62 | STATUS_NO_TABLE_SPACE = 6, 63 | STATUS_MESSAGE_TOO_SHORT = 7, 64 | STATUS_UNIMPLEMENTED_REQUEST = 8, 65 | STATUS_REQUEST_FORMAT_ERROR = 9, 66 | STATUS_RESPONSE_FORMAT_ERROR = 10, 67 | STATUS_COULDNT_CONNECT = 11, 68 | STATUS_BACKUP_BAD_SEGMENT_ID = 12, 69 | /// Returned by backups when they cannot (or do not wish to) allocate 70 | /// space for a segment replica. 71 | STATUS_BACKUP_OPEN_REJECTED = 13, 72 | STATUS_BACKUP_SEGMENT_OVERFLOW = 14, 73 | STATUS_BACKUP_MALFORMED_SEGMENT = 15, 74 | STATUS_SEGMENT_RECOVERY_FAILED = 16, 75 | 76 | /// Indicates that a server is not prepared to handle a request at 77 | /// the present time; the caller should retry at a later time. This 78 | /// status can be returned under many different situations, such as 79 | /// (a) the server is out of resources to execute the request, or 80 | /// (b) the server is not sure it actually has authority to execute 81 | /// the request, and is checking with the coordinator. 82 | STATUS_RETRY = 17, 83 | STATUS_SERVICE_NOT_AVAILABLE = 18, 84 | STATUS_TIMEOUT = 19, 85 | 86 | /// Indicates that server to which an RPC is directed either never existed, 87 | /// has come and gone, or is currently in crashed state. The server is not 88 | /// in a position to respond to RPCs and probably never will be again 89 | /// (unless the id hasn't yet existed; once a server crashes its id will 90 | /// never be reused). 91 | STATUS_SERVER_NOT_UP = 20, 92 | STATUS_INTERNAL_ERROR = 21, 93 | 94 | /// Indicates that the object chosen for an operation does not match the 95 | /// associated requirements. Therefore the chosen object is invalid. 96 | STATUS_INVALID_OBJECT = 22, 97 | /// Indicates that a tablet does not exist. This status is of relevance 98 | /// when doing split or merge operations on tablets are executed. 99 | STATUS_TABLET_DOESNT_EXIST = 23, 100 | /// Indicates that the logic to partition tablets was invoked without a 101 | /// preceeding invocation to start reading replicas off of disk. 102 | STATUS_PARTITION_BEFORE_READ = 24, 103 | /// Indicates that an RPC was intended for a particular server id, but 104 | /// was actually sent to a different server id. 105 | STATUS_WRONG_SERVER = 25, 106 | /// Indicates that the server sending an RPC is not present in the 107 | /// server list of the RPC recipient. Used to help servers discover 108 | /// that they are zombies (the rest of the cluster thinks a zombie 109 | /// is dead, but the zombie thinks it's still alive), so they don't 110 | /// continue servicing requests when other servers have already 111 | /// taken over their tablets. See "Zombies" in designNotes. 112 | STATUS_CALLER_NOT_IN_CLUSTER = 26, 113 | /// Indicates that a single request was too big to fit in an rpc and 114 | /// thus could not be sent/carried out. 115 | STATUS_REQUEST_TOO_LARGE = 27, 116 | 117 | /// Indicates that the server does not know about (and is not responsible 118 | /// for) a given indexlet, but that it may exist elsewhere in the system. 119 | STATUS_UNKNOWN_INDEXLET = 28, 120 | 121 | /// Indicates that an index containing the given index key cannot be found. 122 | STATUS_UNKNOWN_INDEX = 29, 123 | 124 | /// Indicates that a parameter provided by the client is invalid (for 125 | /// example: it is outside allowed bounds). 126 | STATUS_INVALID_PARAMETER = 30, 127 | 128 | STATUS_MAX_VALUE = 30, 129 | 130 | // Note: if you add a new status value you must make the following 131 | // additional updates: 132 | // * Modify STATUS_MAX_VALUE to have a value equal to the largest 133 | // defined status value, and make sure its definition is the last one 134 | // in the list. STATUS_MAX_VALUE is used primarily for testing. 135 | // * Add new entries in the tables "messages" and "symbols" in Status.cc. 136 | // * Add a new exception class to ClientException.h 137 | // * Add a new "case" to ClientException::throwException to map from 138 | // the status value to a status-specific ClientException subclass. 139 | // * In the Java bindings, add a static class for the exception to 140 | // ClientException.java 141 | // * Add a case for the status of the exception to throw the exception in 142 | // ClientException.java 143 | // * Add the exception to the Status enum in Status.java, making 144 | // sure the status is in the correct position corresponding to its status 145 | // code. 146 | } Status; 147 | 148 | extern const char* statusToString(Status status); 149 | extern const char* statusToSymbol(Status status); 150 | 151 | #ifdef __cplusplus 152 | } // namespace RAMCloud 153 | #endif 154 | 155 | #endif // RAMCLOUD_STATUS_H 156 | -------------------------------------------------------------------------------- /src/app/ramcloud/app_ramcloud.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuyuyu101/wheatserver/25dfb895f1d18ad7b7727aae24f0e65be4708c27/src/app/ramcloud/app_ramcloud.h -------------------------------------------------------------------------------- /src/app/static/app_static_file.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef WHEATSERVER_APP_STATIC_FILE_H 6 | #define WHEATSERVER_APP_STATIC_FILE_H 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/app/wheatredis/hashkit.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "redis.h" 10 | 11 | extern void md5_signature(const unsigned char *key, unsigned int length, unsigned char *result); 12 | 13 | static uint32_t ketamaHash(const char *key, size_t key_length, uint32_t alignment) 14 | { 15 | unsigned char results[16]; 16 | 17 | md5_signature((unsigned char*)key, (int)key_length, results); 18 | 19 | return ((uint32_t) (results[3 + alignment * 4] & 0xFF) << 24) 20 | | ((uint32_t) (results[2 + alignment * 4] & 0xFF) << 16) 21 | | ((uint32_t) (results[1 + alignment * 4] & 0xFF) << 8) 22 | | (results[0 + alignment * 4] & 0xFF); 23 | } 24 | 25 | // hashAdd is used when a new redis server add to rebalance tokens 26 | int hashAdd(struct redisServer *server, wstr ip, int port, int id) 27 | { 28 | size_t ninstance, ntoken_per_instance, extra_ntoken, target_idx, ntoken; 29 | struct token *token, *tokens; 30 | struct redisInstance *instance, *target_instance; 31 | struct redisInstance add_instance; 32 | size_t *sub_ntoken; 33 | int i; 34 | 35 | if (initInstance(&add_instance, id, ip, port, DIRTY) == WHEAT_WRONG) 36 | return WHEAT_WRONG; 37 | arrayPush(server->instances, &add_instance); 38 | 39 | // `sub_ntoken`'s element is the amount of tokens every instance should 40 | // take out 41 | ninstance = narray(server->instances); 42 | sub_ntoken = wmalloc(sizeof(int)*ninstance); 43 | ntoken_per_instance = WHEAT_KEYSPACE / ninstance; 44 | extra_ntoken = WHEAT_KEYSPACE % ninstance; 45 | for (i = 0; i < ninstance; i++) { 46 | instance = arrayIndex(server->instances,i); 47 | sub_ntoken[i] = instance->ntoken - ntoken_per_instance; 48 | } 49 | if (!extra_ntoken) { 50 | for (i = 0; i < extra_ntoken; i++) { 51 | sub_ntoken[i]--; 52 | } 53 | } 54 | 55 | target_idx = ninstance-1; 56 | extra_ntoken = sub_ntoken[target_idx]; 57 | target_instance = arrayIndex(server->instances, target_idx); 58 | token = tokens = &server->tokens[0]; 59 | while (extra_ntoken) { 60 | size_t skip = random() % ntoken_per_instance + 1; 61 | while (skip--) { 62 | token = &tokens[token->next_instance]; 63 | } 64 | if (token->instance_id != target_idx && 65 | sub_ntoken[token->instance_id]) { 66 | wheatLog(WHEAT_DEBUG, "token: %d", token->pos); 67 | instance = arrayIndex(server->instances, token->instance_id); 68 | token->instance_id = target_idx; 69 | instance->ntoken--; 70 | target_instance->ntoken++; 71 | } 72 | } 73 | 74 | ntoken = 0; 75 | for (i = 0; i < ninstance; i++) { 76 | instance = arrayIndex(server->instances,i); 77 | ntoken += instance->ntoken; 78 | } 79 | ASSERT(ntoken == WHEAT_KEYSPACE); 80 | 81 | wfree(sub_ntoken); 82 | return WHEAT_OK; 83 | } 84 | 85 | // Only called when config-server is not specifyed. Means that WheatRedis is 86 | // firstly running on your system. 87 | int hashInit(struct redisServer *server) 88 | { 89 | ASSERT(narray(server->instances) >= server->nbackup); 90 | struct redisInstance *instance; 91 | size_t ntoken, ninstance, swap; 92 | int i, prev_idx; 93 | struct token *tokens = server->tokens, *last_token = NULL; 94 | 95 | ntoken = WHEAT_KEYSPACE; 96 | tokens = wmalloc(sizeof(struct token)*ntoken); 97 | if (!tokens) 98 | return WHEAT_WRONG; 99 | server->tokens = tokens; 100 | 101 | ninstance = narray(server->instances); 102 | for (i = 0; i < ntoken; ++i) { 103 | instance = arrayIndex(server->instances, i%ninstance); 104 | tokens[i].next_instance = UINT32_MAX; // An impossible value 105 | tokens[i].pos = i; 106 | tokens[i].instance_id = i%ninstance; 107 | instance->ntoken++; 108 | } 109 | 110 | // shuffle 111 | for (i = 0; i < ntoken; i++) 112 | { 113 | size_t j = i + rand() / (RAND_MAX / (ntoken - i) + 1); 114 | swap = tokens[j].instance_id; 115 | tokens[j].instance_id = tokens[i].instance_id; 116 | tokens[i].instance_id = swap; 117 | } 118 | 119 | // Populate each token.next_instance to quicker search node process 120 | prev_idx = 0; 121 | i = 1; 122 | last_token = &tokens[ntoken-1]; 123 | while (last_token->next_instance == UINT32_MAX) { 124 | if (tokens[i%ntoken].instance_id != tokens[prev_idx].instance_id) { 125 | for (; prev_idx < i && prev_idx < ntoken; ++prev_idx) 126 | tokens[prev_idx].next_instance = i%ntoken; 127 | } 128 | ++i; 129 | } 130 | server->ntoken = ntoken; 131 | return WHEAT_OK; 132 | } 133 | 134 | struct token *hashDispatch(struct redisServer *server, struct slice *key) 135 | { 136 | uint32_t hash = ketamaHash((const char *)key->data, key->len, 0); 137 | 138 | return &server->tokens[hash%WHEAT_KEYSPACE]; 139 | } 140 | -------------------------------------------------------------------------------- /src/app/wheatredis/redis.h: -------------------------------------------------------------------------------- 1 | // WheatRedis module 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_APP_REDIS_CLUSTER_REDIS_H 8 | #define WHEATSERVER_APP_REDIS_CLUSTER_REDIS_H 9 | 10 | #include "../application.h" 11 | #include "../../protocol/redis/proto_redis.h" 12 | 13 | #define WHEAT_KEYSPACE 1024 14 | #define WHEAT_SERVE_WAIT_MILLISECONDS 100 15 | 16 | #define DIRTY 1 17 | #define NONDIRTY 0 18 | 19 | // struct token like virtual node in consistent hash 20 | struct token { 21 | size_t pos; 22 | // `server_idx` is simply the offset of redisServer.instances 23 | size_t instance_id; 24 | // `next_instance` indicates the next real server token not virtual token. 25 | size_t next_instance; 26 | }; 27 | 28 | struct configServer; 29 | 30 | struct redisServer { 31 | size_t max_id; 32 | size_t nbackup; 33 | long timeout; 34 | 35 | // `config_server` only valid in init period, if finishing init, 36 | // `config_server` will be release and set NULL 37 | struct configServer *config_server; 38 | size_t live_instances; 39 | // append new redisUnit to the end of list and keep `message_center` 40 | // time ordered. `message_center` is the owener of redisUnit, so you 41 | // have responsibility to free it 42 | struct list *message_center; 43 | // defer some connections, and now when outer connections comes and 44 | // server in config, we will append connection to pending_conns. 45 | struct list *pending_conns; 46 | struct array *instances; 47 | struct token *tokens; 48 | // Now must be WHEAT_KEYSPACE 49 | size_t ntoken; 50 | int is_serve; 51 | }; 52 | 53 | struct redisInstance { 54 | // Unique identification ID, we use this id to track data owner 55 | size_t id; 56 | wstr ip; 57 | int port; 58 | 59 | struct list *wait_units; 60 | size_t ntoken; 61 | 62 | // Below fields all about the connection between redis and client, 63 | // it will lose effect when connection failed. 64 | struct client *redis_client; 65 | time_t timeout_duration; 66 | // means the amount of timeout response under this instance 67 | int ntimeout; 68 | // reliability is used to choose read instance who's reliability is 69 | // max. And in another condition, when write command responses are 70 | // different select max reliability instance from responses. 71 | // `reliability` is affected by timeout, lose connection times 72 | int reliability; 73 | unsigned live:1; 74 | // When a instance keep timeout_duration larger than threshold value, 75 | // this instance is marked as `dirty`. Only when `instance->ntimeout` 76 | // recover to zero, `is_dirty` is cleared. 77 | // Read command will choose another non-dirty instance to send command 78 | unsigned is_dirty:1; 79 | }; 80 | 81 | struct token *hashDispatch(struct redisServer *server, struct slice *key); 82 | int hashInit(struct redisServer *server); 83 | int hashAdd(struct redisServer *server, wstr ip, int port, int id); 84 | int initInstance(struct redisInstance *instance, size_t, wstr, int, int); 85 | struct client *connectConfigServer(char *option); 86 | void appendToPendingConn(struct conn *c); 87 | 88 | struct configServer *configServerCreate(struct client *client, int use_redis); 89 | void configServerDealloc(struct configServer *config_server); 90 | int getServerFromRedis(struct redisServer *); 91 | int configFromFile(struct redisServer *server); 92 | int handleConfig(struct redisServer *server, struct conn *c); 93 | int isStartServe(struct redisServer *server); 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /src/app/wsgi/app_wsgi.h: -------------------------------------------------------------------------------- 1 | // WSGI Server module 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_WSGI_H 8 | #define WHEATSERVER_WSGI_H 9 | /* ========== wsgi callback ========== */ 10 | #include 11 | #include "../../wstr.h" 12 | #include "../../slice.h" 13 | #include "../../protocol/http/proto_http.h" 14 | 15 | struct conn; 16 | 17 | struct response { 18 | PyObject_HEAD 19 | /* All fields are private */ 20 | struct conn *c; 21 | }; 22 | 23 | typedef struct { 24 | PyObject_HEAD 25 | PyObject *filelike; 26 | int blocksize; 27 | } FileWrapper; 28 | 29 | typedef struct { 30 | PyObject_HEAD 31 | struct conn *c; 32 | size_t readed; 33 | const struct slice *curr; 34 | int pos; 35 | } InputStream; 36 | 37 | struct wsgiData { 38 | PyObject *environ; 39 | void *response; 40 | struct array *body_items; 41 | char *err; 42 | }; 43 | 44 | PyTypeObject responseType; 45 | PyTypeObject FileWrapper_Type; 46 | PyTypeObject InputStream_Type; 47 | 48 | #ifndef PyMODINIT_FUNC 49 | #define PyMODINIT_FUNC void 50 | #endif 51 | PyMODINIT_FUNC 52 | init_wsgisup(void); 53 | PyObject *createEnviron(struct conn *c); 54 | void wsgiCallClose(PyObject *result); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/app/wsgi/wsgiinput.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include 6 | #include "../../slice.h" 7 | #include "app_wsgi.h" 8 | 9 | /* Find occurrence of a character within our buffers */ 10 | static int InputStream_findChar(InputStream *self, struct slice *s, int c) 11 | { 12 | uint8_t *found = memchr(s->data, c, s->len); 13 | if (found != NULL) { 14 | return (int)(found - s->data); 15 | } 16 | 17 | return -1; 18 | } 19 | 20 | /* Consume characters between self->pos and newPos, returning it as a 21 | new Python string. */ 22 | static PyObject *InputStream_consume(InputStream *self, int size) 23 | { 24 | PyObject *result; 25 | char *data; 26 | size_t total = 0; 27 | 28 | if (size <= 0 || !self->curr) 29 | return PyString_FromString(""); 30 | result = PyString_FromStringAndSize(NULL, size); 31 | data = PyString_AS_STRING(result); 32 | do { 33 | if (self->pos >= self->curr->len) { 34 | self->curr = httpGetBodyNext(self->c); 35 | self->pos = 0; 36 | } 37 | size_t remaining = self->curr->len - self->pos; 38 | size_t copyed = size > remaining ? remaining : size; 39 | memcpy(data+total, self->curr->data+self->pos, copyed); 40 | size -= copyed; 41 | total += copyed; 42 | self->pos += copyed; 43 | } while(size > 0); 44 | self->readed += total; 45 | return result; 46 | } 47 | 48 | static void InputStream_dealloc(InputStream *self) 49 | { 50 | self->ob_type->tp_free((PyObject *)self); 51 | } 52 | 53 | static PyObject *InputStream_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 54 | { 55 | InputStream *self; 56 | 57 | self = (InputStream *)type->tp_alloc(type, 0); 58 | if (self != NULL) { 59 | self->c = NULL; 60 | self->pos = 0; 61 | self->readed = 0; 62 | } 63 | 64 | return (PyObject *)self; 65 | } 66 | 67 | /* InputStream constructor. Expects to be passed the parent Request and 68 | the received Content-Length. */ 69 | static int InputStream_init(InputStream *self, PyObject *args, PyObject *kwds) 70 | { 71 | PyObject *c; 72 | 73 | if (!PyArg_ParseTuple(args, "O!", &PyCObject_Type, &c)) 74 | return -1; 75 | 76 | self->c = PyCObject_AsVoidPtr(c); 77 | self->curr = httpGetBodyNext(self->c); 78 | return 0; 79 | } 80 | 81 | /* read() implementation */ 82 | static PyObject *InputStream_read(InputStream *self, PyObject *args) 83 | { 84 | int size = -1; 85 | long remaining; 86 | 87 | if (!PyArg_ParseTuple(args, "|i:read", &size)) 88 | return NULL; 89 | 90 | if (size <= 0 || !self->curr) 91 | return PyString_FromString(""); 92 | 93 | remaining = httpBodyGetSize(self->c) - self->readed; 94 | size = remaining > size ? size : remaining; 95 | 96 | return InputStream_consume(self, size); 97 | } 98 | 99 | /* readline() implementation. Supports "size" argument not required by 100 | WSGI spec (but now required by Python 2.5's cgi module) */ 101 | static PyObject *InputStream_readline(InputStream *self, PyObject *args) 102 | { 103 | long size = -1; 104 | size_t remaining; 105 | 106 | if (!PyArg_ParseTuple(args, "|i:readline", &size)) 107 | return NULL; 108 | 109 | if (size <= 0) 110 | return PyString_FromString(""); 111 | 112 | remaining = self->curr->len - self->pos + httpBodyGetSize(self->c); 113 | size = remaining > size ? size : remaining; 114 | 115 | return InputStream_consume(self, size); 116 | } 117 | 118 | /* readlines() implementation. Supports "hint" argument. */ 119 | static PyObject *InputStream_readlines(InputStream *self, PyObject *args) 120 | { 121 | int hint = 0, total = 0; 122 | PyObject *lines = NULL, *args2 = NULL, *line; 123 | ssize_t len, ret; 124 | 125 | if (!PyArg_ParseTuple(args, "|i:readlines", &hint)) 126 | return NULL; 127 | 128 | if ((lines = PyList_New(0)) == NULL) 129 | return NULL; 130 | 131 | if ((args2 = PyTuple_New(0)) == NULL) 132 | goto bad; 133 | 134 | if ((line = InputStream_readline(self, args2)) == NULL) 135 | goto bad; 136 | 137 | while ((len = PyString_GET_SIZE(line)) > 0) { 138 | ret = PyList_Append(lines, line); 139 | Py_DECREF(line); 140 | if (ret) 141 | goto bad; 142 | total += len; 143 | if (hint > 0 && total >= hint) 144 | break; 145 | 146 | if ((line = InputStream_readline(self, args2)) == NULL) 147 | goto bad; 148 | } 149 | 150 | Py_DECREF(line); 151 | Py_DECREF(args2); 152 | 153 | return lines; 154 | 155 | bad: 156 | Py_XDECREF(args2); 157 | Py_XDECREF(lines); 158 | return NULL; 159 | } 160 | 161 | /* __iter__() implementation. Simply returns self. */ 162 | static PyObject *InputStream_iter(InputStream *self) 163 | { 164 | Py_INCREF(self); 165 | return (PyObject *)self; 166 | } 167 | 168 | /* next() implementation for iteration protocol support */ 169 | static PyObject *InputStream_iternext(InputStream *self) 170 | { 171 | PyObject *line, *args; 172 | 173 | if ((args = PyTuple_New(0)) == NULL) 174 | return NULL; 175 | 176 | line = InputStream_readline(self, args); 177 | Py_DECREF(args); 178 | if (line == NULL) 179 | return NULL; 180 | 181 | if (PyString_GET_SIZE(line) == 0) { 182 | Py_DECREF(line); 183 | PyErr_Clear(); 184 | return NULL; 185 | } 186 | 187 | return line; 188 | } 189 | 190 | static PyMethodDef InputStream_methods[] = { 191 | {"read", (PyCFunction)InputStream_read, METH_VARARGS, 192 | "Read from this input stream" }, 193 | {"readline", (PyCFunction)InputStream_readline, METH_VARARGS, 194 | "Read a line from this input stream" }, 195 | {"readlines", (PyCFunction)InputStream_readlines, METH_VARARGS, 196 | "Read lines from this input stream" }, 197 | { NULL } 198 | }; 199 | 200 | PyTypeObject InputStream_Type = { 201 | PyObject_HEAD_INIT(NULL) 202 | 0, /*ob_size*/ 203 | "_wsgisup.InputStream", /*tp_name*/ 204 | sizeof(InputStream), /*tp_basicsize*/ 205 | 0, /*tp_itemsize*/ 206 | (destructor)InputStream_dealloc, /*tp_dealloc*/ 207 | 0, /*tp_print*/ 208 | 0, /*tp_getattr*/ 209 | 0, /*tp_setattr*/ 210 | 0, /*tp_compare*/ 211 | 0, /*tp_repr*/ 212 | 0, /*tp_as_number*/ 213 | 0, /*tp_as_sequence*/ 214 | 0, /*tp_as_mapping*/ 215 | 0, /*tp_hash */ 216 | 0, /*tp_call*/ 217 | 0, /*tp_str*/ 218 | 0, /*tp_getattro*/ 219 | 0, /*tp_setattro*/ 220 | 0, /*tp_as_buffer*/ 221 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /*tp_flags*/ 222 | "wsgi.input implementation", /* tp_doc */ 223 | 0, /* tp_traverse */ 224 | 0, /* tp_clear */ 225 | 0, /* tp_richcompare */ 226 | 0, /* tp_weaklistoffset */ 227 | (getiterfunc)InputStream_iter, /* tp_iter */ 228 | (iternextfunc)InputStream_iternext, /* tp_iternext */ 229 | InputStream_methods, /* tp_methods */ 230 | 0, /* tp_members */ 231 | 0, /* tp_getset */ 232 | 0, /* tp_base */ 233 | 0, /* tp_dict */ 234 | 0, /* tp_descr_get */ 235 | 0, /* tp_descr_set */ 236 | 0, /* tp_dictoffset */ 237 | (initproc)InputStream_init, /* tp_init */ 238 | 0, /* tp_alloc */ 239 | InputStream_new, /* tp_new */ 240 | }; 241 | 242 | -------------------------------------------------------------------------------- /src/app/wsgi/wsgiwrapper.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include 6 | #include "app_wsgi.h" 7 | 8 | static void FileWrapper_dealloc(FileWrapper *self) 9 | { 10 | PyObject *tmp; 11 | 12 | tmp = (PyObject *)self->filelike; 13 | self->filelike = NULL; 14 | Py_XDECREF(tmp); 15 | 16 | self->ob_type->tp_free((PyObject *)self); 17 | } 18 | 19 | static PyObject * 20 | FileWrapper_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 21 | { 22 | FileWrapper *self; 23 | 24 | self = (FileWrapper *)type->tp_alloc(type, 0); 25 | if (self != NULL) { 26 | self->filelike = NULL; 27 | } 28 | 29 | return (PyObject *)self; 30 | } 31 | 32 | static int 33 | FileWrapper_init(FileWrapper *self, PyObject *args, PyObject *kwds) 34 | { 35 | PyObject *filelike; 36 | int blocksize = 4096; 37 | 38 | if (!PyArg_ParseTuple(args, "O|i", &filelike, &blocksize)) 39 | return -1; 40 | 41 | Py_INCREF(filelike); 42 | self->filelike = filelike; 43 | self->blocksize = blocksize; 44 | 45 | return 0; 46 | } 47 | 48 | /* __iter__() implementation. Simply returns self. */ 49 | static PyObject * 50 | FileWrapper_iter(FileWrapper *self) 51 | { 52 | Py_INCREF(self); 53 | return (PyObject *)self; 54 | } 55 | 56 | /* next() implementation for iteration protocol support. Calls read() 57 | on the file-like and emits strings according to the blocksize. 58 | This is only used for compatibility (the normal path uses 59 | wsgiSendFile()). */ 60 | static PyObject * 61 | FileWrapper_iternext(FileWrapper *self) 62 | { 63 | PyObject *pRead, *args, *data; 64 | long len; 65 | 66 | if ((pRead = PyObject_GetAttrString(self->filelike, "read")) == NULL) 67 | return NULL; 68 | 69 | if ((args = Py_BuildValue("(i)", self->blocksize)) == NULL) { 70 | Py_DECREF(pRead); 71 | return NULL; 72 | } 73 | 74 | data = PyObject_CallObject(pRead, args); 75 | Py_DECREF(args); 76 | Py_DECREF(pRead); 77 | if (data == NULL) 78 | return NULL; 79 | 80 | len = PyString_Size(data); 81 | if (PyErr_Occurred()) { 82 | Py_DECREF(data); 83 | return NULL; 84 | } 85 | 86 | if (len == 0) { 87 | Py_DECREF(data); 88 | PyErr_Clear(); 89 | return NULL; 90 | } 91 | 92 | return data; 93 | } 94 | 95 | /* Calls the file-like's close() method, if present. */ 96 | static PyObject * 97 | FileWrapper_close(FileWrapper *self, PyObject *args) 98 | { 99 | PyObject *pClose, *args2, *result; 100 | 101 | if (PyObject_HasAttrString(self->filelike, "close")) { 102 | if ((pClose = PyObject_GetAttrString(self->filelike, "close")) == NULL) 103 | return NULL; 104 | 105 | if ((args2 = PyTuple_New(0)) == NULL) { 106 | Py_DECREF(pClose); 107 | return NULL; 108 | } 109 | 110 | result = PyObject_CallObject(pClose, args2); 111 | Py_DECREF(args2); 112 | Py_DECREF(pClose); 113 | if (result == NULL) 114 | return NULL; 115 | } 116 | 117 | Py_INCREF(Py_None); 118 | return Py_None; 119 | } 120 | 121 | static PyMethodDef FileWrapper_methods[] = { 122 | { "close", (PyCFunction)FileWrapper_close, METH_VARARGS, 123 | "Calls the file-like object's close method" }, 124 | { NULL } 125 | }; 126 | 127 | PyTypeObject FileWrapper_Type = { 128 | PyObject_HEAD_INIT(NULL) 129 | 0, /*ob_size*/ 130 | "_wsgisup.FileWrapper", /*tp_name*/ 131 | sizeof(FileWrapper), /*tp_basicsize*/ 132 | 0, /*tp_itemsize*/ 133 | (destructor)FileWrapper_dealloc, /*tp_dealloc*/ 134 | 0, /*tp_print*/ 135 | 0, /*tp_getattr*/ 136 | 0, /*tp_setattr*/ 137 | 0, /*tp_compare*/ 138 | 0, /*tp_repr*/ 139 | 0, /*tp_as_number*/ 140 | 0, /*tp_as_sequence*/ 141 | 0, /*tp_as_mapping*/ 142 | 0, /*tp_hash */ 143 | 0, /*tp_call*/ 144 | 0, /*tp_str*/ 145 | 0, /*tp_getattro*/ 146 | 0, /*tp_setattro*/ 147 | 0, /*tp_as_buffer*/ 148 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /*tp_flags*/ 149 | "wsgi.file_wrapper implementation", /* tp_doc */ 150 | 0, /* tp_traverse */ 151 | 0, /* tp_clear */ 152 | 0, /* tp_richcompare */ 153 | 0, /* tp_weaklistoffset */ 154 | (getiterfunc)FileWrapper_iter, /* tp_iter */ 155 | (iternextfunc)FileWrapper_iternext, /* tp_iternext */ 156 | FileWrapper_methods, /* tp_methods */ 157 | 0, /* tp_members */ 158 | 0, /* tp_getset */ 159 | 0, /* tp_base */ 160 | 0, /* tp_dict */ 161 | 0, /* tp_descr_get */ 162 | 0, /* tp_descr_set */ 163 | 0, /* tp_dictoffset */ 164 | (initproc)FileWrapper_init, /* tp_init */ 165 | 0, /* tp_alloc */ 166 | FileWrapper_new, /* tp_new */ 167 | }; 168 | -------------------------------------------------------------------------------- /src/array.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "array.h" 10 | #include "memalloc.h" 11 | 12 | struct array { 13 | size_t nelem; 14 | uint8_t *elements; 15 | size_t elem_size; 16 | size_t capacity; 17 | }; 18 | 19 | struct array *arrayCreate(size_t elem_size, size_t cap) 20 | { 21 | size_t data_len = elem_size * cap; 22 | struct array *arr = wmalloc(sizeof(struct array)); 23 | arr->nelem = 0; 24 | arr->elem_size = elem_size; 25 | arr->elements = wmalloc(data_len); 26 | arr->capacity = cap; 27 | return arr; 28 | } 29 | 30 | struct array *arrayDup(struct array *a) 31 | { 32 | struct array *dup; 33 | size_t len; 34 | 35 | dup = wmalloc(sizeof(struct array)); 36 | memcpy(dup, a, sizeof(struct array)); 37 | len = a->elem_size * a->capacity; 38 | dup->elements = wmalloc(len); 39 | memcpy(dup->elements, a->elements, len); 40 | return dup; 41 | } 42 | 43 | void arrayDealloc(struct array *arr) 44 | { 45 | wfree(arr->elements); 46 | wfree(arr); 47 | } 48 | 49 | void *arrayIndex(struct array *a, size_t pos) 50 | { 51 | if (pos >= a->nelem) 52 | return NULL; 53 | size_t p = pos * a->elem_size; 54 | return a->elements+p; 55 | } 56 | 57 | void arraySet(struct array *a, void *data, size_t p) 58 | { 59 | if (p > a->nelem) 60 | return ; 61 | size_t pos = p * a->elem_size; 62 | if (a->nelem == a->capacity) { 63 | size_t new_cap = a->capacity * 2; 64 | uint8_t *p = wrealloc(a->elements, a->elem_size*new_cap); 65 | a->elements = p; 66 | a->capacity = new_cap; 67 | } 68 | memcpy(a->elements+pos, data, a->elem_size); 69 | if (p == a->nelem) 70 | a->nelem++; 71 | } 72 | 73 | void arrayPush(struct array *a, void *data) 74 | { 75 | arraySet(a, data, a->nelem); 76 | } 77 | 78 | void *arrayPop(struct array *a) 79 | { 80 | return a->elements+a->elem_size*(--a->nelem); 81 | } 82 | 83 | void *arrayTop(struct array *a) 84 | { 85 | return arrayIndex(a, 0); 86 | } 87 | 88 | void *arrayLast(struct array *a) 89 | { 90 | return arrayIndex(a, a->nelem-1); 91 | } 92 | 93 | void arrayEach(struct array *a, void(*func)(void *)) 94 | { 95 | size_t len = a->nelem; 96 | uint8_t *pos = a->elements; 97 | while (len--) { 98 | func(pos); 99 | pos += a->elem_size; 100 | } 101 | } 102 | 103 | void arrayEach2(struct array *a, void(*func)(void *item, void *data), void *data) 104 | { 105 | size_t len = a->nelem; 106 | uint8_t *pos = a->elements; 107 | while (len--) { 108 | func(pos, data); 109 | pos += a->elem_size; 110 | } 111 | } 112 | 113 | size_t narray(struct array *a) 114 | { 115 | return a->nelem; 116 | } 117 | 118 | void *arrayData(struct array *a) 119 | { 120 | return a->elements; 121 | } 122 | 123 | #ifdef ARRAY_TEST_MAIN 124 | #include "test_help.h" 125 | #include "stdlib.h" 126 | #include "stdio.h" 127 | 128 | int main(int argc, const char *argv[]) 129 | { 130 | { 131 | struct array *a = arrayCreate(sizeof(int), 10); 132 | int i; 133 | for (i = 0; i < 11; ++i) { 134 | arrayPush(a, &i); 135 | } 136 | test_cond("arrayTop", *(int *)(arrayTop(a)) == 0); 137 | for (i = 0; i < 10; ++i) { 138 | int *j = arrayPop(a); 139 | test_cond("arrayPop", *j == 10-i); 140 | } 141 | i = 10; 142 | arrayPush(a, &i); 143 | i = 9; 144 | arrayPush(a, &i); 145 | test_cond("arrayTop", *(int *)(arrayTop(a)) == 0); 146 | arrayDealloc(a); 147 | } 148 | test_report(); 149 | return 0; 150 | } 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /src/array.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef WHEATSERVER_ARRAY_H 6 | #define WHEATSERVER_ARRAY_H 7 | 8 | struct array; 9 | 10 | // `array` is a structure like traditional linear table. The elements in array 11 | // is ordered and continuously. 12 | // 13 | // | element 1 | element 2| element 3 | ... | element N| 14 | // 15 | // If you want iterate the whole array, there are two ways for you and try to 16 | // use one of them: 17 | // 18 | // 1. Iterate directly 19 | // struct array *array = arrayCreate(...); 20 | // ... 21 | // arrayPush(...); 22 | // arrayPush(...); 23 | // arrayPush(...); 24 | // ... 25 | // struct data *data, *data_array = arrayData(array); 26 | // size_t len = narray(array); 27 | // for (i = 0; i < len; ++i) { 28 | // data = data_array[i]; 29 | // ... 30 | // } 31 | // 32 | // 2. Iterate by function 33 | // static void operateOnArrayItem(void *item) 34 | // { 35 | // struct data *data = item; 36 | // ... 37 | // } 38 | // static void operateOnArrayItem2(void *item, void *client_data) 39 | // { 40 | // struct data *data = item; 41 | // ... 42 | // } 43 | // arrayEach(array, operateOnArrayItem); 44 | // OR 45 | // arrayEach2(array, operateOnArrayItem2, client_data); 46 | // 47 | // You can expand array transparently. If you are sensitive to performance on 48 | // memory moving(expanding array may cause memory move), you should create a 49 | // enough big on argument for `capacity`. 50 | 51 | // Modified methods 52 | struct array *arrayCreate(size_t elem_size, size_t cap); 53 | struct array *arrayDup(struct array *a); 54 | void arrayDealloc(struct array *arr); 55 | void arraySet(struct array *a, void *data, size_t p); 56 | void arrayPush(struct array *a, void *data); 57 | void *arrayPop(struct array *a); 58 | 59 | // Unmodified methods 60 | void *arrayIndex(struct array *a, size_t pos); 61 | void *arrayTop(struct array *a); 62 | void *arrayLast(struct array *a); 63 | void arrayEach(struct array *a, void(*func)(void *)); 64 | void arrayEach2(struct array *a, void(*func)(void *, void *), void *data); 65 | void *arrayData(struct array *a); 66 | size_t narray(struct array *a); 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/build_module_table.py: -------------------------------------------------------------------------------- 1 | import sys 2 | modules = sys.argv[1].split() 3 | codes = '#include "wheatserver.h"\n' 4 | for module in modules: 5 | codes += 'extern struct moduleAttr %s;\n' % module 6 | codes += 'struct moduleAttr *ModuleTable[] = {\n' 7 | for module in modules: 8 | codes += '&%s,\n' % module 9 | codes += 'NULL};\n' 10 | f = open("modules.c", 'r') 11 | old_codes = f.read(10000) 12 | f.close() 13 | if old_codes != codes: 14 | f = open("modules.c", 'w') 15 | f.write(codes) 16 | f.close() 17 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | // debug.c implemented functions used by debug and trace 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include "wheatserver.h" 8 | #include 9 | 10 | void wheat_stacktrace(int skip_count) 11 | { 12 | void *stack[64]; 13 | char **symbols; 14 | int size, i, j; 15 | 16 | size = backtrace(stack, 64); 17 | symbols = backtrace_symbols(stack, size); 18 | if (symbols == NULL) { 19 | return; 20 | } 21 | 22 | skip_count++; /* skip the current frame also */ 23 | 24 | for (i = skip_count, j = 0; i < size; i++, j++) { 25 | wheatLog(WHEAT_WARNING, "[%d] %s", j, symbols[i]); 26 | } 27 | 28 | wfree(symbols); 29 | } 30 | 31 | void wheat_assert(const char *cond, const char *file, int line, int panic) 32 | { 33 | wheatLog(WHEAT_WARNING, "assert '%s' failed @ (%s, %d)", cond, file, line); 34 | if (panic) { 35 | wheat_stacktrace(1); 36 | abort(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/debug.h: -------------------------------------------------------------------------------- 1 | // Implemented functions used by debug and trace 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_DEBUG_H 8 | #define WHEATSERVER_DEBUG_H 9 | 10 | void wheat_stacktrace(int skip_count); 11 | void wheat_assert(const char *cond, const char *file, int line, int panic); 12 | 13 | #define ASSERT(_x) do { \ 14 | if (!(_x)) { \ 15 | wheat_assert(#_x, __FILE__, __LINE__, 1); \ 16 | } \ 17 | } while (0) 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/dict.h: -------------------------------------------------------------------------------- 1 | // Hash table implemetation 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_DICT_H 8 | #define WHEATSERVER_DICT_H 9 | 10 | #define DICT_OK 0 11 | #define DICT_WRONG 1 12 | 13 | #define DICT_HT_INITIAL_SIZE 16 14 | #define DICT_FORCE_RESIZE_RATIO 5 15 | 16 | #include 17 | 18 | // Key, value ownership: 19 | // If `keyDup` in `type` field in struct dict is specified, dict will duplicate 20 | // the key. If `keyDup` is empty, caller must guarantee the live of key. 21 | // Like `keyDup`, `valDup` is the same. 22 | // 23 | // In general, you could create dictType like wstrDictType: don't set `keyDup` 24 | // and `valDup`, but set `keyDestructor` and `valDestructor`. In this way, you 25 | // can pass ownership to dict and needn't to free it. And it reduce unnecessary 26 | // allocation and release operates. 27 | // 28 | // Performance: 29 | // You should be cautious of insert much key and value pairs to dict. If you 30 | // are sensitive to performance, try to call dictExpand prepared. It will 31 | // expand slots in dict. 32 | // 33 | // Traversing hash table example: 34 | // struct dict *d = dictCreate(&wstrDictType); 35 | // ... 36 | // dictAdd(key, value); 37 | // dictAdd(key, value); 38 | // ... 39 | // struct dictIterator *iter = dictGetIterator(d); 40 | // struct dictEntry *entry; 41 | // while ((entry = dictNext(iter)) != NULL) { 42 | // key = dictGetKey(entry); 43 | // value = dictGetVal(entry); 44 | // ... 45 | // } 46 | // dictReleaseIterator(iter); 47 | 48 | struct dictEntry { 49 | void *key; 50 | union { 51 | void *val; 52 | uint64_t u64; 53 | int64_t s64; 54 | } v; 55 | struct dictEntry *next; 56 | }; 57 | 58 | struct dictType { 59 | unsigned int (*hashFunction)(const void *key); 60 | void *(*keyDup)(const void *key); 61 | void *(*valDup)(const void *obj); 62 | int (*keyCompare)(const void *key1, const void *key2); 63 | void (*keyDestructor)(void *key); 64 | void (*valDestructor)(void *obj); 65 | }; 66 | 67 | // This is our hash table structure. 68 | // `size`: the slots for hash 69 | // `sizemask`: the upper bound for `size`, using for getting slot within `size` 70 | // `used`: the number of entries. 71 | // `type`: the methods for operate key and value 72 | struct dict { 73 | struct dictEntry **table; 74 | unsigned long size; 75 | unsigned long sizemask; 76 | unsigned long used; 77 | struct dictType *type; 78 | }; 79 | 80 | struct dictIterator { 81 | struct dict *d; 82 | int table, index; 83 | struct dictEntry *entry, *nextEntry; 84 | }; 85 | 86 | /* ========== Macros ========== */ 87 | 88 | #define dictOwnKey(d) ((d)->type->keyDup != NULL) 89 | #define dictOwnValue(d) ((d)->type->valDup != NULL) 90 | 91 | #define dictFreeVal(d, entry) \ 92 | if ((d)->type->valDestructor && (entry)->v.val) \ 93 | (d)->type->valDestructor((entry)->v.val) 94 | 95 | #define dictSetVal(d, entry, _val_) do { \ 96 | if ((d)->type->valDup) \ 97 | entry->v.val = (d)->type->valDup(_val_); \ 98 | else \ 99 | entry->v.val = (_val_); \ 100 | } while(0) 101 | 102 | #define dictSetKey(d, entry, _key_) do { \ 103 | if ((d)->type->keyDup) \ 104 | entry->key = (d)->type->keyDup(_key_); \ 105 | else \ 106 | entry->key = (_key_); \ 107 | } while(0) 108 | 109 | #define dictFreeKey(d, entry) \ 110 | if ((d)->type->keyDestructor && entry->key) \ 111 | (d)->type->keyDestructor((entry)->key) 112 | 113 | #define dictCompareKeys(d, key1, key2) \ 114 | (((d)->type->keyCompare) ? \ 115 | (d)->type->keyCompare(key1, key2) : \ 116 | (key1) == (key2)) 117 | 118 | #define dictHashKey(d, key) (d)->type->hashFunction(key) 119 | #define dictGetKey(he) ((he)->key) 120 | #define dictGetVal(he) ((he)->v.val) 121 | #define dictGetSignedIntegerVal(he) ((he)->v.s64) 122 | #define dictGetUnsignedIntegerVal(he) ((he)->v.u64) 123 | #define dictSlots(d) ((d)->size) 124 | #define dictSize(d) ((d)->used) 125 | 126 | // Base Dict API 127 | struct dict *dictCreate(struct dictType*); 128 | int dictAdd(struct dict *d, void *key, void *val); 129 | int dictReplace(struct dict *d, void *key, void *val, int *replace); 130 | int dictDelete(struct dict *d, const void *key); 131 | // dictDeleteNoFree is used if you don't want to free key and value when 132 | // `keyDestructor` and `valDestructor` is set. 133 | int dictDeleteNoFree(struct dict *d, const void *key); 134 | struct dictEntry * dictFind(struct dict *d, const void *key); 135 | void *dictFetchValue(struct dict *d, const void *key); 136 | void dictRelease(struct dict *d); 137 | 138 | // Iterator operators 139 | struct dictIterator *dictGetIterator(struct dict *d); 140 | struct dictEntry *dictNext(struct dictIterator *iter); 141 | void dictReleaseIterator(struct dictIterator *iter); 142 | 143 | // Low level API 144 | int dictExpand(struct dict *d, unsigned long size); 145 | struct dictEntry *dictAddRaw(struct dict *d, void *key); 146 | struct dictEntry *dictReplaceRaw(struct dict *d, void *key); 147 | void dictClear(struct dict *d); 148 | 149 | // Debug use, it may crash affecting the whole application 150 | void dictPrintStats(struct dict *d); 151 | void dictPrint(struct dict *d); 152 | 153 | // Hash function, using them for customize key, value pair 154 | unsigned int dictGenHashFunction(const void *key, int len); 155 | unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len); 156 | unsigned int dictGetHashFunctionSeed(void); 157 | void dictSetHashFunctionSeed(unsigned int initval); 158 | 159 | #endif 160 | -------------------------------------------------------------------------------- /src/event.c: -------------------------------------------------------------------------------- 1 | // A simple event-driven library learn from Redis. It support multi 2 | // event-notify API and easy to extend more platforms. 3 | // 4 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 7 | 8 | #include "wheatserver.h" 9 | 10 | #ifdef HAVE_EPOLL 11 | #include "event_epoll.c" 12 | #else 13 | #ifdef HAVE_KQUEUE 14 | #include "event_kqueue.c" 15 | #else 16 | #include "event_select.c" 17 | #endif 18 | #endif 19 | 20 | struct evcenter *eventcenterInit(int nevent) 21 | { 22 | struct event *events = NULL; 23 | struct evcenter *center = NULL; 24 | struct fired_event *fired_events = NULL; 25 | void *api_state = NULL; 26 | 27 | center = wmalloc(sizeof(struct evcenter)); 28 | if (center == NULL) { 29 | wheatLog(WHEAT_WARNING, "center create failed: %s", strerror(errno)); 30 | return NULL; 31 | } 32 | events = wmalloc(nevent*sizeof(struct event)); 33 | fired_events = wmalloc(nevent*sizeof(struct fired_event)); 34 | if (fired_events == NULL || events == NULL) { 35 | wheatLog(WHEAT_WARNING, "event and fired_event create failed: %s", 36 | strerror(errno)); 37 | goto cleanup; 38 | } 39 | memset(events, 0, nevent*sizeof(struct event)); 40 | memset(fired_events, 0, nevent*sizeof(struct fired_event)); 41 | 42 | api_state = eventInit(nevent); 43 | if (!api_state) { 44 | wheatLog(WHEAT_WARNING, "naive event init failed", strerror(errno)); 45 | goto cleanup; 46 | } 47 | 48 | center->nevent = nevent; 49 | center->events = events; 50 | center->fired_events = fired_events; 51 | center->apidata = api_state; 52 | 53 | return center; 54 | 55 | cleanup: 56 | if (center) 57 | wfree(center); 58 | if (fired_events) 59 | wfree(fired_events); 60 | if (events) 61 | wfree(events); 62 | return NULL; 63 | } 64 | 65 | void eventcenterDealloc(struct evcenter *center) 66 | { 67 | eventDeinit(center->apidata); 68 | wfree(center->events); 69 | wfree(center); 70 | } 71 | 72 | int createEvent(struct evcenter *center, int fd, int mask, eventProc *proc, void *client_data) 73 | { 74 | if (fd >= center->nevent) { 75 | errno = ERANGE; 76 | return WHEAT_WRONG; 77 | } 78 | struct event *event = ¢er->events[fd]; 79 | 80 | if (addEvent(center, fd, mask) == -1) 81 | return WHEAT_WRONG; 82 | event->mask |= mask; 83 | if (mask & EVENT_READABLE) event->read_proc = proc; 84 | if (mask & EVENT_WRITABLE) event->write_proc = proc; 85 | event->client_data = client_data; 86 | return WHEAT_OK; 87 | } 88 | 89 | void deleteEvent(struct evcenter *center, int fd, int mask) 90 | { 91 | if (fd >= center->nevent) return; 92 | struct event *event = ¢er->events[fd]; 93 | 94 | if (event->mask == EVENT_NONE) return; 95 | event->mask = event->mask & (~mask); 96 | delEvent(center, fd, mask); 97 | } 98 | 99 | int processEvents(struct evcenter *center, int timeout_millionseconds) 100 | { 101 | struct timeval tv; 102 | int j, processed, numevents, mask, fd, rfired; 103 | struct event *event; 104 | 105 | if (timeout_millionseconds > 0) { 106 | tv.tv_sec = timeout_millionseconds / 1000; 107 | tv.tv_usec = (timeout_millionseconds % 1000) * 1000; 108 | } 109 | else { 110 | tv.tv_sec = 0; 111 | tv.tv_usec = 0; 112 | } 113 | 114 | processed = 0; 115 | numevents = eventWait(center, &tv); 116 | for (j = 0; j < numevents; j++) { 117 | event = ¢er->events[center->fired_events[j].fd]; 118 | mask = center->fired_events[j].mask; 119 | fd = center->fired_events[j].fd; 120 | rfired = 0; 121 | 122 | /* note the fe->mask & mask & ... code: maybe an already processed 123 | * event removed an element that fired and we still didn't 124 | * processed, so we check if the event is still valid. */ 125 | if (event->mask & mask & EVENT_READABLE) { 126 | rfired = 1; 127 | event->read_proc(center, fd, event->client_data, mask); 128 | } 129 | if (event->mask & mask & EVENT_WRITABLE) { 130 | if (!rfired || event->read_proc != event->write_proc) 131 | event->write_proc(center, fd, event->client_data, mask); 132 | } 133 | processed++; 134 | } 135 | return processed; 136 | } 137 | -------------------------------------------------------------------------------- /src/event.h: -------------------------------------------------------------------------------- 1 | // A simple event-driven library learn from Redis. It support multi 2 | // event-notify API and easy to extend more platforms. 3 | // 4 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 7 | 8 | #ifndef WHEATSERVER_EVENT_H_ 9 | #define WHEATSERVER_EVENT_H_ 10 | 11 | #ifdef __APPLE__ 12 | #include 13 | #endif 14 | 15 | // We use epoll, kqueue, evport, select in descending order by performance. 16 | #ifdef __linux__ 17 | #define HAVE_EPOLL 1 18 | #endif 19 | 20 | #if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__) 21 | #define HAVE_KQUEUE 1 22 | #endif 23 | 24 | #ifdef __sun 25 | #include 26 | #ifdef _DTRACE_VERSION 27 | #define HAVE_EVPORT 1 28 | #endif 29 | #endif 30 | 31 | #define EVENT_NONE 0 32 | #define EVENT_READABLE 1 33 | #define EVENT_WRITABLE 2 34 | 35 | struct evcenter; 36 | 37 | // Attention: 38 | // This event library use file description as index to search correspond event 39 | // in `events` and `fired_events`. So it's important to estimate a suitable 40 | // capacity in calling eventcenterInit(capacity). 41 | 42 | typedef void eventProc(struct evcenter *center, int fd, void *client_data, 43 | int mask); 44 | 45 | struct event { 46 | int mask; 47 | void *client_data; 48 | eventProc *read_proc; 49 | eventProc *write_proc; 50 | }; 51 | 52 | struct fired_event { 53 | int mask; 54 | int fd; 55 | }; 56 | 57 | struct evcenter { 58 | int nevent; 59 | struct event *events; 60 | struct fired_event *fired_events; 61 | void *apidata; 62 | }; 63 | 64 | struct evcenter *eventcenterInit(int nevent); 65 | void eventcenterDealloc(struct evcenter *center); 66 | 67 | int createEvent(struct evcenter *center, int fd, int mask, eventProc *proc, 68 | void *client_data); 69 | void deleteEvent(struct evcenter *center, int fd, int mask); 70 | int processEvents(struct evcenter *center, int timeout_milliseconds); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src/event_epoll.c: -------------------------------------------------------------------------------- 1 | // Linux epoll(2) based event.c module 2 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 3 | // Use of this source code is governed by a BSD-style license that can be 4 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 5 | 6 | /* Linux epoll(2) based ae.c module 7 | * 8 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 9 | * All rights reserved. 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * * Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * * Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * * Neither the name of Redis nor the names of its contributors may be used 20 | * to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | * POSSIBILITY OF SUCH DAMAGE. 34 | */ 35 | 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | #include "event.h" 42 | #include "memalloc.h" 43 | 44 | struct apiState { 45 | int epfd; 46 | struct epoll_event *events; 47 | }; 48 | 49 | static struct apiState *eventInit(int nevent) { 50 | struct apiState *state = wmalloc(sizeof(struct apiState)); 51 | 52 | if (!state) return NULL; 53 | state->events = wmalloc(sizeof(struct epoll_event)*nevent); 54 | if (!state->events) { 55 | wfree(state); 56 | return NULL; 57 | } 58 | state->epfd = epoll_create(1024); /* 1024 is just an hint for the kernel */ 59 | if (state->epfd == -1) { 60 | wfree(state->events); 61 | wfree(state); 62 | return NULL; 63 | } 64 | return state; 65 | } 66 | 67 | static void eventDeinit(struct apiState *state) { 68 | close(state->epfd); 69 | wfree(state->events); 70 | wfree(state); 71 | } 72 | 73 | static int addEvent(struct evcenter *center, int fd, int mask) { 74 | struct apiState *state = center->apidata; 75 | struct epoll_event ee; 76 | /* If the fd was already monitored for some event, we need a MOD 77 | * operation. Otherwise we need an ADD operation. */ 78 | int op = center->events[fd].mask == EVENT_NONE ? 79 | EPOLL_CTL_ADD : EPOLL_CTL_MOD; 80 | 81 | ee.events = 0; 82 | mask |= center->events[fd].mask; /* Merge old events */ 83 | if (mask & EVENT_READABLE) ee.events |= EPOLLIN; 84 | if (mask & EVENT_WRITABLE) ee.events |= EPOLLOUT; 85 | ee.data.u64 = 0; /* avoid valgrind warning */ 86 | ee.data.fd = fd; 87 | if (epoll_ctl(state->epfd, op, fd, &ee) == -1) return -1; 88 | return 0; 89 | } 90 | 91 | static void delEvent(struct evcenter *center, int fd, int delmask) { 92 | struct apiState *state = center->apidata; 93 | struct epoll_event ee; 94 | int mask = center->events[fd].mask & (~delmask); 95 | 96 | ee.events = 0; 97 | if (mask & EVENT_READABLE) ee.events |= EPOLLIN; 98 | if (mask & EVENT_WRITABLE) ee.events |= EPOLLOUT; 99 | ee.data.u64 = 0; /* avoid valgrind warning */ 100 | ee.data.fd = fd; 101 | if (mask != EVENT_NONE) { 102 | epoll_ctl(state->epfd, EPOLL_CTL_MOD, fd, &ee); 103 | } else { 104 | /* Note, Kernel < 2.6.9 requires a non null event pointer even for 105 | * EPOLL_CTL_DEL. */ 106 | epoll_ctl(state->epfd, EPOLL_CTL_DEL, fd, &ee); 107 | } 108 | } 109 | 110 | static int eventWait(struct evcenter *center, struct timeval *tvp) { 111 | struct apiState *state = center->apidata; 112 | int retval, numevents = 0; 113 | 114 | retval = epoll_wait(state->epfd, state->events, center->nevent, 115 | tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1); 116 | if (retval > 0) { 117 | int j; 118 | 119 | numevents = retval; 120 | for (j = 0; j < numevents; j++) { 121 | int mask = 0; 122 | struct epoll_event *e = state->events + j; 123 | 124 | if (e->events & EPOLLIN) mask |= EVENT_READABLE; 125 | if (e->events & EPOLLOUT) mask |= EVENT_WRITABLE; 126 | if (e->events & EPOLLERR) mask |= EVENT_WRITABLE; 127 | if (e->events & EPOLLHUP) mask |= EVENT_WRITABLE; 128 | center->fired_events[j].fd = e->data.fd; 129 | center->fired_events[j].mask = mask; 130 | } 131 | } 132 | return numevents; 133 | } 134 | -------------------------------------------------------------------------------- /src/event_kqueue.c: -------------------------------------------------------------------------------- 1 | // BSD kqueue(2) based event.c module 2 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 3 | // Use of this source code is governed by a BSD-style license that can be 4 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 5 | /* 6 | * 7 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * * Redistributions of source code must retain the above copyright notice, 14 | * this list of conditions and the following disclaimer. 15 | * * Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * * Neither the name of Redis nor the names of its contributors may be used 19 | * to endorse or promote products derived from this software without 20 | * specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | * POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | #include "event.h" 41 | #include "memalloc.h" 42 | 43 | struct apiState { 44 | int kqfd; 45 | struct kevent *events; 46 | }; 47 | 48 | static struct apiState *eventInit(int nevent) 49 | { 50 | struct kevent *events = NULL; 51 | int ep; 52 | struct apiState *state = NULL; 53 | 54 | ep = kqueue(); 55 | if (ep < 0) { 56 | return NULL; 57 | } 58 | 59 | state = wmalloc(sizeof(struct apiState)); 60 | if (!state) { 61 | close(ep); 62 | return NULL; 63 | } 64 | 65 | events = wmalloc(nevent*sizeof(struct kevent)); 66 | if (events == NULL) { 67 | close(ep); 68 | wfree(state); 69 | return NULL; 70 | } 71 | 72 | state->kqfd = ep; 73 | state->events = events; 74 | return state; 75 | } 76 | 77 | static void eventDeinit(struct apiState *data) 78 | { 79 | close(data->kqfd); 80 | wfree(data->events); 81 | wfree(data); 82 | } 83 | 84 | static int addEvent(struct evcenter *center, int fd, int mask) { 85 | struct apiState *state = center->apidata; 86 | struct kevent ke; 87 | 88 | if (mask & EVENT_READABLE) { 89 | EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); 90 | if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 91 | } 92 | if (mask & EVENT_WRITABLE) { 93 | EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); 94 | if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1; 95 | } 96 | return 0; 97 | } 98 | 99 | static void delEvent(struct evcenter *center, int fd, int mask) { 100 | struct apiState *state = center->apidata; 101 | struct kevent ke; 102 | 103 | if (mask & EVENT_READABLE) { 104 | EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); 105 | kevent(state->kqfd, &ke, 1, NULL, 0, NULL); 106 | } 107 | 108 | if (mask & EVENT_WRITABLE) { 109 | EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); 110 | kevent(state->kqfd, &ke, 1, NULL, 0, NULL); 111 | } 112 | } 113 | 114 | 115 | static int eventWait(struct evcenter *center, struct timeval *tvp) { 116 | struct apiState *state = center->apidata; 117 | int retval, numevents = 0; 118 | 119 | if (tvp != NULL) { 120 | struct timespec timeout; 121 | timeout.tv_sec = tvp->tv_sec; 122 | timeout.tv_nsec = tvp->tv_usec * 1000; 123 | retval = kevent(state->kqfd, NULL, 0, state->events, center->nevent, 124 | &timeout); 125 | } else { 126 | retval = kevent(state->kqfd, NULL, 0, state->events, center->nevent, 127 | NULL); 128 | } 129 | 130 | if (retval > 0) { 131 | int j; 132 | 133 | numevents = retval; 134 | for (j = 0; j < numevents; j++) { 135 | int mask = 0; 136 | struct kevent *e = state->events + j; 137 | 138 | if (e->filter == EVFILT_READ) mask |= EVENT_READABLE; 139 | if (e->filter == EVFILT_WRITE) mask |= EVENT_WRITABLE; 140 | center->fired_events[j].fd = (int)e->ident; 141 | center->fired_events[j].mask = mask; 142 | } 143 | } 144 | return numevents; 145 | } 146 | -------------------------------------------------------------------------------- /src/event_select.c: -------------------------------------------------------------------------------- 1 | // Select()-based event.c module. 2 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 3 | // Use of this source code is governed by a BSD-style license that can be 4 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 5 | /* 6 | * 7 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * * Redistributions of source code must retain the above copyright notice, 14 | * this list of conditions and the following disclaimer. 15 | * * Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * * Neither the name of Redis nor the names of its contributors may be used 19 | * to endorse or promote products derived from this software without 20 | * specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | * POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | #include "memalloc.h" 41 | #include "event.h" 42 | 43 | struct apiState { 44 | fd_set rfds, wfds; 45 | /* We need to have a copy of the fd sets as it's not safe to reuse 46 | * FD sets after select(). */ 47 | fd_set _rfds, _wfds; 48 | int max_fd; 49 | }; 50 | 51 | static struct apiState *eventInit(int nevent) 52 | { 53 | struct apiState *state = wmalloc(sizeof(struct apiState)); 54 | 55 | if (!state) return NULL; 56 | FD_ZERO(&state->rfds); 57 | FD_ZERO(&state->wfds); 58 | state->max_fd = 0; 59 | return state; 60 | } 61 | 62 | static void eventDeinit(struct apiState *data) 63 | { 64 | wfree(data); 65 | } 66 | 67 | static int addEvent(struct evcenter *center, int fd, int mask) 68 | { 69 | struct apiState *state = center->apidata; 70 | if (mask & EVENT_READABLE) FD_SET(fd, &state->rfds); 71 | if (mask & EVENT_WRITABLE) FD_SET(fd, &state->wfds); 72 | if (fd > state->max_fd) 73 | state->max_fd = fd; 74 | return 0; 75 | } 76 | 77 | static void delEvent(struct evcenter *center, int fd, int mask) 78 | { 79 | struct apiState *state = center->apidata; 80 | if (mask & EVENT_READABLE) FD_CLR(fd, &state->rfds); 81 | if (mask & EVENT_WRITABLE) FD_CLR(fd, &state->wfds); 82 | } 83 | 84 | static int eventWait(struct evcenter *center, struct timeval *tvp) { 85 | struct apiState *state = center->apidata; 86 | int retval, j, numevents = 0; 87 | 88 | memcpy(&state->_rfds, &state->rfds, sizeof(fd_set)); 89 | memcpy(&state->_wfds, &state->wfds, sizeof(fd_set)); 90 | 91 | retval = select(state->max_fd+1, &state->_rfds, &state->_wfds, NULL, tvp); 92 | if (retval > 0) { 93 | for (j = 0; j <= state->max_fd; j++) { 94 | int mask = 0; 95 | struct event *fe = ¢er->events[j]; 96 | 97 | if (fe->mask == EVENT_NONE) continue; 98 | if (fe->mask & EVENT_READABLE && FD_ISSET(j, &state->_rfds)) 99 | mask |= EVENT_READABLE; 100 | if (fe->mask & EVENT_WRITABLE && FD_ISSET(j, &state->_wfds)) 101 | mask |= EVENT_WRITABLE; 102 | center->fired_events[numevents].fd = j; 103 | center->fired_events[numevents].mask = mask; 104 | numevents++; 105 | } 106 | } 107 | return numevents; 108 | } 109 | -------------------------------------------------------------------------------- /src/hook.c: -------------------------------------------------------------------------------- 1 | // Used by modules to hook actions to master process 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include "hook.h" 8 | #include "wheatserver.h" 9 | 10 | void initHookCenter() 11 | { 12 | Server.hook_center = wmalloc(sizeof(struct hookCenter)); 13 | if (Server.hook_center == NULL) { 14 | wheatLog(WHEAT_WARNING, "init hookcenter failed"); 15 | halt(1); 16 | return ; 17 | } 18 | 19 | Server.hook_center->afterinit = createList(); 20 | Server.hook_center->whenready = createList(); 21 | Server.hook_center->prefork_worker = createList(); 22 | Server.hook_center->afterfork_worker = createList(); 23 | Server.hook_center->whenwake = createList(); 24 | Server.hook_center->beforesleep = createList(); 25 | } 26 | -------------------------------------------------------------------------------- /src/hook.h: -------------------------------------------------------------------------------- 1 | // Used by modules to hook actions to master process 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_HOOK_H 8 | #define WHEATSERVER_HOOK_H 9 | 10 | #include "list.h" 11 | 12 | /* typedef hook functions */ 13 | 14 | typedef void afterInitProc(); 15 | typedef void whenReadyProc(); 16 | typedef void preForkWorkerProc(); 17 | typedef void afterForkWorkerProc(); 18 | typedef void whenWakeProc(); 19 | typedef void beforeSleepProc(); 20 | 21 | struct hookCenter { 22 | struct list *afterinit; 23 | struct list *whenready; 24 | struct list *prefork_worker; 25 | struct list *afterfork_worker; 26 | struct list *whenwake; 27 | struct list *beforesleep; 28 | }; 29 | 30 | void initHookCenter(); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | // Implementation of double linked-list 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_LIST_H 8 | #define WHEATSERVER_LIST_H 9 | 10 | #include 11 | 12 | // Ownership: 13 | // List duplicate value when `dup` set. 14 | // List free value when `free` set. 15 | // List compare value when `match` set. 16 | // 17 | // User guide: 18 | // Don't recommand you to set `dup` method, and if you want to reduce jobs on 19 | // elements free, you could set free method and for ease. 20 | // 21 | // Traversing example: 22 | // struct list *l = createList(); 23 | // appendToListTail(l); 24 | // appendToListTail(l); 25 | // appendToListTail(l); 26 | // ... 27 | // struct listIterator *iter = listGetIterator(l, START_HEAD); 28 | // struct listNode *node; 29 | // while ((node = listNext(iter)) != NULL) { 30 | // struct data *data = listNodeValue(node); 31 | // ... 32 | // } 33 | // freeListIterator(iter); 34 | // 35 | // Delete guide: 36 | // Because list is implemented by double link, and it's convient to remove 37 | // node from linked list. You could save the node for deleted and call: 38 | // removeListNode(l, node); 39 | 40 | struct listNode { 41 | struct listNode *next; 42 | struct listNode *prev; 43 | void *value; 44 | }; 45 | 46 | struct list { 47 | struct listNode *first, *last; 48 | void *(*dup)(void *ptr); 49 | int (*match)(void *ptr, void *key); 50 | void (*free)(void *ptr); 51 | unsigned long len; 52 | }; 53 | 54 | #define listLength(list) ((list)->len) 55 | #define listFirst(list) ((list)->first) 56 | #define listLast(list) ((list)->last) 57 | #define listNodeValue(list) ((void*)((list)->value)) 58 | 59 | #define listSetDup(list, m) ((list)->dup = (m)) 60 | #define listSetFree(list, m) ((list)->free = (m)) 61 | #define listSetMatch(list, m) ((list)->match = (m)) 62 | 63 | struct listIterator { 64 | struct listNode *next; 65 | int direction; 66 | }; 67 | 68 | #define START_HEAD 0 69 | #define START_TAIL 1 70 | 71 | // Base list API 72 | struct list *createList(); 73 | void freeList(struct list *); 74 | struct listNode *appendToListTail(struct list *, void *ptr); 75 | struct listNode *insertToListHead(struct list *, void *ptr); 76 | void removeListNode(struct list *l, struct listNode *node); 77 | struct listNode *searchListKey(struct list *l, void *key); 78 | void listClear(struct list *l); 79 | 80 | // Traversing list 81 | struct listIterator *listGetIterator(struct list *list, int direction); 82 | void listRotate(struct list *l); 83 | struct listNode *listNext(struct listIterator *iter); 84 | void freeListIterator(struct listIterator *iter); 85 | void listEach(struct list *l, void(*func)(void *)); 86 | void listEach2(struct list *l, void(*func)(void *, void*), void *data); 87 | 88 | // Debug use 89 | void listPrint(struct list *list); 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | // Implementation of log messages handler 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include "wheatserver.h" 8 | 9 | #define WHEAT_MAX_LOGFILE (100*1024*1024) 10 | 11 | // TODO: When reload config file if specify logfile is stdout, 12 | // now code can't redirect back to stdout. 13 | void logRedirect() 14 | { 15 | if (Server.logfile && strcasecmp(Server.logfile, "stdout")) { 16 | FILE *fp; 17 | fclose(stdin); 18 | fclose(stdout); 19 | fclose(stderr); 20 | fp = freopen(Server.logfile, "a", stdout); 21 | if (!fp) { 22 | wheatLog(WHEAT_WARNING, "file redirect failed %s", 23 | strerror(errno)); 24 | halt(1); 25 | } 26 | setbuf(fp, NULL); 27 | fp = freopen(Server.logfile, "a", stderr); 28 | if (!fp) { 29 | wheatLog(WHEAT_WARNING, "file redirect failed %s", 30 | strerror(errno)); 31 | halt(1); 32 | } 33 | setbuf(fp, NULL); 34 | } 35 | } 36 | 37 | // Now, logging will open file and flush it to disk if logfile is file, close 38 | // it finally. It may slower process jobs and not effective. 39 | // TODO: Open file and keep file description. Export flush policy to users. 40 | void wheatLogRaw(int level, const char *msg) 41 | { 42 | static char *c = ".-* #"; 43 | FILE *fp; 44 | char buf[64]; 45 | off_t len; 46 | int rawmode = (level & WHEAT_LOG_RAW); 47 | 48 | level &= 0xff; /* clear flags */ 49 | if (level < Server.verbose) return; 50 | 51 | if (Server.logfile == NULL || !strcasecmp(Server.logfile, "stdout")) { 52 | fp = stdout; 53 | } else { 54 | fp = fopen(Server.logfile, "a"); 55 | if (getFileSizeAndMtime(fileno(fp), &len, NULL)) { 56 | if (len > WHEAT_MAX_LOGFILE) { 57 | char newname[WHEATSERVER_PATH_LEN]; 58 | time_t now; 59 | fclose(fp); 60 | now = time(NULL); 61 | snprintf(newname, sizeof(newname), "%s.%s", Server.logfile, ctime(&now)); 62 | rename(Server.logfile, newname); 63 | fp = fopen(Server.logfile, "a"); 64 | } 65 | } 66 | } 67 | if (!fp) return; 68 | 69 | if (rawmode) { 70 | fprintf(fp,"%s",msg); 71 | } else { 72 | size_t off; 73 | struct timeval tv; 74 | 75 | gettimeofday(&tv,NULL); 76 | off = strftime(buf, sizeof(buf), "%d %b %H:%M:%S.", localtime(&tv.tv_sec)); 77 | snprintf(buf+off, sizeof(buf)-off, "%03d", (int)tv.tv_usec/1000); 78 | fprintf(fp,"[%d] %s %c %s\n", (int)getpid(), buf, c[level], msg); 79 | } 80 | fflush(fp); 81 | 82 | if (fp != stdout) fclose(fp); 83 | } 84 | 85 | // `level`: different log level setting will decide whether log message or not. 86 | // * WHEAT_DEBUG 87 | // * WHEAT_VERBOSE 88 | // * WHEAT_NOTICE 89 | // * WHEAT_WARNING 90 | void wheatLog(int level, const char *fmt, ...) 91 | { 92 | va_list ap; 93 | char msg[WHEATSERVER_MAX_LOG_LEN]; 94 | int ret; 95 | 96 | if ((level&0xff) < Server.verbose) return; 97 | 98 | va_start(ap, fmt); 99 | ret = vsnprintf(msg, sizeof(msg), fmt, ap); 100 | va_end(ap); 101 | 102 | wheatLogRaw(level, msg); 103 | } 104 | -------------------------------------------------------------------------------- /src/memalloc.c: -------------------------------------------------------------------------------- 1 | // Memory allocation and release wrapper 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef DEBUG 12 | #include 13 | static int alloc[33000]; 14 | #define STAT_ALLOC(size) do { \ 15 | if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1)); \ 16 | alloc[size]++; \ 17 | }while(0) 18 | 19 | void allocPrint() 20 | { 21 | int i; 22 | for (i = 0; i < 33000; i++) { 23 | if (alloc[i] != 0) 24 | printf("size %d: %d times\n", i, alloc[i]); 25 | } 26 | } 27 | #else 28 | #define STAT_ALLOC(size) do { \ 29 | if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1)); \ 30 | }while(0) 31 | #endif 32 | 33 | static uint64_t MemorySize = 0; 34 | 35 | void *wmalloc(size_t size) 36 | { 37 | STAT_ALLOC(size); 38 | MemorySize += size; 39 | void *p = malloc(size); 40 | return p; 41 | } 42 | 43 | void *wrealloc(void *old, size_t size) 44 | { 45 | STAT_ALLOC(size); 46 | MemorySize += size; 47 | void *p = realloc(old, size); 48 | return p; 49 | } 50 | 51 | void *wcalloc(size_t count, size_t size) 52 | { 53 | void *p = wmalloc(size*count); 54 | return p; 55 | } 56 | 57 | void wfree(void *ptr) 58 | { 59 | free(ptr); 60 | } 61 | -------------------------------------------------------------------------------- /src/memalloc.h: -------------------------------------------------------------------------------- 1 | // Memory allocation and release wrapper 2 | // In order to utilize different memory management implemention, wrapping 3 | // memory API to make upper layer transparently 4 | // 5 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 6 | // Use of this source code is governed by a BSD-style license that can be 7 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 8 | 9 | #ifndef WHEATSERVER_MEMALLOC 10 | #define WHEATSERVER_MEMALLOC 11 | 12 | void *wmalloc(size_t size); 13 | void *wrealloc(void *old, size_t size); 14 | void *wcalloc(size_t count, size_t size); 15 | void wfree(void *ptr); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/mempool.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | -------------------------------------------------------------------------------- /src/modules.c: -------------------------------------------------------------------------------- 1 | #include "wheatserver.h" 2 | extern struct moduleAttr AppWsgiAttr; 3 | extern struct moduleAttr AppStaticAttr; 4 | extern struct moduleAttr AppRedisAttr; 5 | extern struct moduleAttr AppRamcloudAttr; 6 | extern struct moduleAttr ProtocolHttpAttr; 7 | extern struct moduleAttr ProtocolMemcacheAttr; 8 | extern struct moduleAttr ProtocolRedisAttr; 9 | extern struct moduleAttr SyncWorkerAttr; 10 | extern struct moduleAttr AsyncWorkerAttr; 11 | struct moduleAttr *ModuleTable[] = { 12 | &AppWsgiAttr, 13 | &AppStaticAttr, 14 | &AppRedisAttr, 15 | &AppRamcloudAttr, 16 | &ProtocolHttpAttr, 17 | &ProtocolMemcacheAttr, 18 | &ProtocolRedisAttr, 19 | &SyncWorkerAttr, 20 | &AsyncWorkerAttr, 21 | NULL}; 22 | -------------------------------------------------------------------------------- /src/net.c: -------------------------------------------------------------------------------- 1 | // Basic TCP socket stuff made a bit less boring 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "net.h" 20 | 21 | static void wheatSetError(char *err, const char *fmt, ...) 22 | { 23 | va_list ap; 24 | 25 | if (!err) return; 26 | va_start(ap, fmt); 27 | vsnprintf(err, NET_ERR_LEN, fmt, ap); 28 | va_end(ap); 29 | } 30 | 31 | static int wheatCreateSocket(char *err, int domain) 32 | { 33 | int s, on = 1; 34 | 35 | if ((s = socket(domain, SOCK_STREAM, 0)) == -1) { 36 | wheatSetError(err, "creating socket: %s", strerror(errno)); 37 | return NET_WRONG; 38 | } 39 | /* Make sure connection-intensive things like the benckmark 40 | * will be able to close/open sockets a zillion of times */ 41 | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { 42 | wheatSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno)); 43 | return NET_WRONG; 44 | } 45 | return s; 46 | } 47 | 48 | #define WHEAT_CONNECT_NONE 0 49 | #define WHEAT_CONNECT_NONBLOCK 1 50 | static int wheatTcpGenericConnect(char *err, char *addr, int port, int flags) 51 | { 52 | int s; 53 | struct sockaddr_in sa; 54 | 55 | if ((s = wheatCreateSocket(err, AF_INET)) == NET_WRONG) 56 | return NET_WRONG; 57 | 58 | sa.sin_family = AF_INET; 59 | sa.sin_port = htons(port); 60 | if (!addr) 61 | addr = "127.0.0.1"; 62 | if (inet_aton(addr, &sa.sin_addr) == 0) { 63 | struct hostent *he; 64 | 65 | he = gethostbyname(addr); 66 | if (he == NULL) { 67 | wheatSetError(err, "can't resolve: %s", addr); 68 | close(s); 69 | return NET_WRONG; 70 | } 71 | memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); 72 | } 73 | if (flags & WHEAT_CONNECT_NONBLOCK) { 74 | if (wheatNonBlock(err,s) != NET_OK) 75 | return NET_WRONG; 76 | } 77 | if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) { 78 | if (errno == EINPROGRESS && 79 | flags & WHEAT_CONNECT_NONBLOCK) 80 | return s; 81 | 82 | wheatSetError(err, "connect: %s", strerror(errno)); 83 | close(s); 84 | return NET_WRONG; 85 | } 86 | return s; 87 | } 88 | 89 | int wheatTcpConnect(char *err, char *addr, int port) 90 | { 91 | return wheatTcpGenericConnect(err,addr,port,WHEAT_CONNECT_NONE); 92 | } 93 | 94 | int wheatTcpNonBlockConnect(char *err, char *addr, int port) 95 | { 96 | return wheatTcpGenericConnect(err, addr, port, WHEAT_CONNECT_NONBLOCK); 97 | } 98 | 99 | static int wheatListen(char *err, int s, struct sockaddr *sa, socklen_t len) { 100 | if (bind(s, sa, len) == -1) { 101 | wheatSetError(err, "bind: %s", strerror(errno)); 102 | close(s); 103 | return NET_WRONG; 104 | } 105 | 106 | /* Use a backlog of 512 entries. We pass 511 to the listen() call because 107 | * the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1); 108 | * which will thus give us a backlog of 512 entries */ 109 | if (listen(s, 511) == -1) { 110 | wheatSetError(err, "listen: %s", strerror(errno)); 111 | close(s); 112 | return NET_WRONG; 113 | } 114 | return NET_OK; 115 | } 116 | 117 | static int wheatGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) { 118 | int fd; 119 | while(1) { 120 | fd = accept(s, sa, len); 121 | if (fd == -1) { 122 | if (errno == EINTR) 123 | continue; 124 | else if (errno == EAGAIN) { 125 | return NET_WRONG; 126 | } 127 | else { 128 | wheatSetError(err, "accept: %s", strerror(errno)); 129 | return NET_WRONG; 130 | } 131 | } 132 | break; 133 | } 134 | return fd; 135 | } 136 | 137 | int wheatNonBlock(char *err, int fd) 138 | { 139 | int flags; 140 | 141 | /* Set the socket nonblocking. 142 | * Note that fcntl(2) for F_GETFL and F_SETFL can't be 143 | * interrupted by a signal. */ 144 | if ((flags = fcntl(fd, F_GETFL)) < 0 ) { 145 | wheatSetError(err, "fcntl(F_GETFL): %s", strerror(errno)); 146 | return NET_WRONG; 147 | } 148 | if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { 149 | wheatSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)); 150 | return NET_WRONG; 151 | } 152 | return NET_OK; 153 | } 154 | 155 | int wheatCloseOnExec(char *err, int fd) 156 | { 157 | int flags; 158 | 159 | if ((flags = fcntl(fd, F_GETFD, 0)) < 0) { 160 | wheatSetError(err, "fcntl(F_GETFL): %s", strerror(errno)); 161 | return NET_WRONG; 162 | } 163 | if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { 164 | wheatSetError(err, "fcntl(F_SETFL, FD_CLOEXEC): %s", strerror(errno)); 165 | return NET_WRONG; 166 | } 167 | return NET_OK; 168 | } 169 | 170 | int wheatSetSendBuffer(char *err, int fd, int buffsize) 171 | { 172 | if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1) 173 | { 174 | wheatSetError(err, "setsockopt SO_SNDBUF: %s", strerror(errno)); 175 | return NET_WRONG; 176 | } 177 | return NET_OK; 178 | } 179 | 180 | int wheatTcpNoDelay(char *err, int fd) 181 | { 182 | int yes = 1; 183 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) 184 | { 185 | wheatSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno)); 186 | return NET_WRONG; 187 | } 188 | return NET_OK; 189 | } 190 | 191 | int wheatTcpKeepAlive(char *err, int fd) 192 | { 193 | int yes = 1; 194 | if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) { 195 | wheatSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno)); 196 | return NET_WRONG; 197 | } 198 | return NET_OK; 199 | } 200 | 201 | /* sizeof(ip) must larger than INET_ADDRSTRLEN(16) */ 202 | int wheatTcpAccept(char *err, int s, char *ip, int *port) { 203 | int fd; 204 | struct sockaddr_in sa; 205 | socklen_t salen = sizeof(sa); 206 | if ((fd = wheatGenericAccept(err, s, (struct sockaddr*)&sa,&salen)) == NET_WRONG) 207 | return NET_WRONG; 208 | 209 | if (ip) inet_ntop(AF_INET, &sa.sin_addr, ip, INET_ADDRSTRLEN); 210 | if (port) *port = ntohs(sa.sin_port); 211 | return fd; 212 | } 213 | 214 | int wheatTcpServer(char *err, char *bind_addr, int port){ 215 | int s; 216 | struct sockaddr_in sa; 217 | 218 | if ((s = wheatCreateSocket(err, AF_INET)) == NET_WRONG) 219 | return NET_WRONG; 220 | 221 | memset(&sa,0,sizeof(sa)); 222 | sa.sin_family = AF_INET; 223 | sa.sin_port = htons(port); 224 | sa.sin_addr.s_addr = htonl(INADDR_ANY); 225 | if (bind_addr && inet_pton(AF_INET, bind_addr, &sa.sin_addr) != 1) { 226 | wheatSetError(err, "wheatTcpServer: invalid bind address"); 227 | close(s); 228 | return NET_WRONG; 229 | } 230 | 231 | if (wheatListen(err, s, (struct sockaddr*)&sa, sizeof(sa)) == NET_WRONG) { 232 | wheatSetError(err, "wheatTcpServer: listen failed"); 233 | close(s); 234 | return NET_WRONG; 235 | } 236 | return s; 237 | } 238 | -------------------------------------------------------------------------------- /src/net.h: -------------------------------------------------------------------------------- 1 | // Basic TCP socket stuff made a bit less boring 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_NET_H 8 | #define WHEATSERVER_NET_H 9 | 10 | #define NET_OK 0 11 | #define NET_WRONG -1 12 | #define NET_ERR_LEN 255 13 | 14 | int wheatNonBlock(char *err, int fd); 15 | int wheatTcpNoDelay(char *err, int fd); 16 | int wheatTcpKeepAlive(char *err, int fd); 17 | int wheatCloseOnExec(char *err, int fd); 18 | int wheatTcpServer(char *err, char *bind_attr, int port); 19 | int wheatTcpConnect(char *err, char *addr, int port); 20 | int wheatTcpNonBlockConnect(char *err, char *addr, int port); 21 | int wheatTcpAccept(char *err, int s, char *ip, int *port); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/networking.c: -------------------------------------------------------------------------------- 1 | // Low level network IO wrapper for use 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include "networking.h" 8 | 9 | /* return -1 means `fd` occurs error or closed, it should be closed 10 | * return 0 means EAGAIN */ 11 | int readBulkFrom(int fd, struct slice *slice) 12 | { 13 | ssize_t nread; 14 | 15 | nread = read(fd, slice->data, slice->len); 16 | if (nread == -1) { 17 | if (errno == EAGAIN) { 18 | nread = 0; 19 | } else { 20 | wheatLog(WHEAT_NOTICE, "Reading from fd %d: %s", fd, strerror(errno)); 21 | return WHEAT_WRONG; 22 | } 23 | } else if (nread == 0) { 24 | wheatLog(WHEAT_DEBUG, "Peer close file descriptor %d", fd); 25 | return WHEAT_WRONG; 26 | } 27 | return (int)nread; 28 | } 29 | 30 | int syncReadBulkFrom(int fd, struct slice *slice) 31 | { 32 | int read = 0; 33 | 34 | do { 35 | read = readBulkFrom(fd, slice); 36 | } while(read == 0); 37 | return read; 38 | } 39 | 40 | /* return -1 means `fd` occurs error or closed, it should be closed 41 | * return 0 means EAGAIN */ 42 | int writeBulkTo(int fd, struct slice *slice) 43 | { 44 | ssize_t nwritten; 45 | 46 | nwritten = write(fd, slice->data, slice->len); 47 | if (nwritten == -1) { 48 | if (errno == EAGAIN) { 49 | nwritten = 0; 50 | } else if (errno == EPIPE) { 51 | wheatLog(WHEAT_DEBUG, "Receive RST, peer closed", strerror(errno)); 52 | return WHEAT_WRONG; 53 | } else { 54 | wheatLog(WHEAT_NOTICE, 55 | "Error writing to client: %s", strerror(errno)); 56 | return WHEAT_WRONG; 57 | } 58 | } 59 | return (int)nwritten; 60 | } 61 | 62 | int syncWriteBulkTo(int fd, struct slice *slice) 63 | { 64 | int totallen, ret; 65 | 66 | totallen = 0; 67 | while (totallen < slice->len) { 68 | ret = writeBulkTo(fd, slice); 69 | if (ret == -1) 70 | return WHEAT_WRONG; 71 | totallen += ret; 72 | } 73 | return totallen; 74 | } 75 | 76 | static void sendReplyToClient(struct evcenter *center, int fd, void *data, int mask) 77 | { 78 | struct masterClient *client = data; 79 | size_t bufpos = 0, totallen = wstrlen(client->response_buf); 80 | ssize_t nwritten = 0; 81 | 82 | while (bufpos < totallen) { 83 | struct slice slice; 84 | sliceTo(&slice, (uint8_t *)client->response_buf, 85 | wstrlen(client->response_buf)); 86 | nwritten = writeBulkTo(client->fd, &slice); 87 | if (nwritten <= 0) 88 | break; 89 | bufpos += nwritten; 90 | } 91 | if (nwritten == -1) { 92 | freeMasterClient(client); 93 | return ; 94 | } 95 | if (bufpos == totallen) { 96 | deleteEvent(center, fd, EVENT_WRITABLE); 97 | } 98 | } 99 | 100 | ssize_t isClientPreparedWrite(int fd, struct evcenter *center, void *c) 101 | { 102 | if (fd <= 0 || createEvent(center, fd, EVENT_WRITABLE, sendReplyToClient, c) == WHEAT_WRONG) 103 | return WHEAT_WRONG; 104 | return WHEAT_OK; 105 | } 106 | 107 | void replyMasterClient(struct masterClient *c, const char *buf, size_t len) 108 | { 109 | if (isClientPreparedWrite(c->fd, Server.master_center, c) == WHEAT_WRONG) 110 | return ; 111 | c->response_buf = wstrCatLen(c->response_buf, buf, len); 112 | } 113 | -------------------------------------------------------------------------------- /src/networking.h: -------------------------------------------------------------------------------- 1 | // Low level network IO wrapper for use 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_NETWORKING_H 8 | #define WHEATSERVER_NETWORKING_H 9 | 10 | #include "wheatserver.h" 11 | 12 | #define WHEAT_IOBUF_LEN 1024 * 4 13 | 14 | // wrapper for read(2) write(2), you should keep buffer slice referenced alive. 15 | int readBulkFrom(int fd, struct slice *slice); 16 | int writeBulkTo(int fd, struct slice *clientbuf); 17 | 18 | // Used by master process for send and receive messages from clients or workers. 19 | struct masterClient; 20 | void replyMasterClient(struct masterClient *c, const char *buf, size_t len); 21 | ssize_t isClientPreparedWrite(int fd, struct evcenter *center, void *c); 22 | int syncWriteBulkTo(int fd, struct slice *slice); 23 | int syncReadBulkFrom(int fd, struct slice *slice); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/portable.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #include "wheatserver.h" 6 | 7 | #ifdef __APPLE__ 8 | /* OS X */ 9 | #include 10 | #include 11 | ssize_t portable_sendfile(int out_fd, int in_fd, off_t off, off_t len) { 12 | if (sendfile(in_fd, out_fd, off, &len, NULL, 0) == -1) { 13 | if (errno != EAGAIN) 14 | return -1; 15 | } 16 | return len; 17 | } 18 | #endif 19 | #ifdef __linux 20 | /* Linux */ 21 | #include 22 | 23 | ssize_t portable_sendfile(int out_fd, int in_fd, off_t off, off_t len) { 24 | ssize_t ret; 25 | if ((ret = sendfile(out_fd, in_fd, &off, len)) == -1) { 26 | if (errno != EAGAIN) 27 | return -1; 28 | else 29 | return 0; 30 | } 31 | return ret; 32 | } 33 | 34 | #endif 35 | 36 | void setProctitle(const char *title) 37 | { 38 | setproctitle("wheatserver: %s %s:%d-%d", title, 39 | Server.bind_addr ? Server.bind_addr : "*", 40 | Server.port_range_start, Server.port_range_end); 41 | } 42 | -------------------------------------------------------------------------------- /src/portable.h: -------------------------------------------------------------------------------- 1 | // Implementation of the portable APIs on different platforms 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_PORTABLE_H 8 | #define WHEATSERVER_PORTABLE_H 9 | 10 | // portable for sendfile(2), support BSD and linux. 11 | // return the length of data sent or -1 means error occurred, return 0 if 12 | // outer_fd is non-blocking means EAGAIN errno. 13 | ssize_t portable_sendfile(int out_fd, int in_fd, off_t, off_t len); 14 | 15 | /* Check if we can use setproctitle(). 16 | * BSD systems have support for it, we provide an implementation for 17 | * Linux and osx. */ 18 | #if (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__) 19 | #define USE_SETPROCTITLE 20 | #endif 21 | 22 | #if (defined __linux || defined __APPLE__) 23 | #define USE_SETPROCTITLE 24 | void setproctitle(const char *fmt, ...); 25 | #endif 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/protocol/http/proto_http.h: -------------------------------------------------------------------------------- 1 | // Http protocol module implemetation 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_PROTO_HTTP_H 8 | #define WHEATSERVER_PROTO_HTTP_H 9 | 10 | #include "../protocol.h" 11 | #include "http_parser.h" 12 | 13 | #define TRANSFER_ENCODING "Transfer-Encoding" 14 | #define CONTENT_LENGTH "Content-Length" 15 | #define CONTENT_TYPE "Content-Type" 16 | #define CONNECTION "Connection" 17 | #define LAST_MODIFIED "Last-Modified" 18 | #define IF_MODIFIED_SINCE "If-Modified-Since" 19 | #define CHUNKED "Chunked" 20 | #define HTTP_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n" 21 | 22 | // Http protocol API 23 | const wstr httpGetPath(struct conn *c); 24 | const wstr httpGetQueryString(struct conn *c); 25 | const struct slice *httpGetBodyNext(struct conn *c); 26 | int httpBodyGetSize(struct conn *c); 27 | const char *httpGetUrlScheme(struct conn *c); 28 | const char *httpGetMethod(struct conn *c); 29 | const char *httpGetProtocolVersion(struct conn *c); 30 | struct dict *httpGetReqHeaders(struct conn *c); 31 | int ishttpHeaderSended(struct conn *c); 32 | int httpGetResStatus(struct conn *c); 33 | void parserForward(wstr value, wstr *h, wstr *p); 34 | int convertHttpDate(time_t date, char *buf, size_t len); 35 | time_t fromHttpDate(char *buf); 36 | int httpSendBody(struct conn *c, const char *data, size_t len); 37 | void fillResInfo(struct conn *c, int status, const char *msg); 38 | int httpSendHeaders(struct conn *c); 39 | void sendResponse500(struct conn *c); 40 | void sendResponse404(struct conn *c); 41 | int appendToResHeaders(struct conn *c, const char *field, 42 | const char *value); 43 | 44 | void logAccess(struct conn *c); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/protocol/memcache/proto_memcache.h: -------------------------------------------------------------------------------- 1 | // Memcache protocol parse implemetation 2 | // 3 | // Copyright (c) 2014 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | #ifndef WHEATSERVER_PROTOCOL_MEMCACHE_PROTO_MEMCACHE_H 7 | #define WHEATSERVER_PROTOCOL_MEMCACHE_PROTO_MEMCACHE_H 8 | 9 | #include "../protocol.h" 10 | 11 | enum memcacheCommand { 12 | REQ_MC_GET, 13 | REQ_MC_GETS, 14 | REQ_MC_DELETE, 15 | REQ_MC_CAS, 16 | REQ_MC_SET, 17 | REQ_MC_ADD, 18 | REQ_MC_REPLACE, 19 | REQ_MC_APPEND, 20 | REQ_MC_PREPEND, 21 | REQ_MC_INCR, 22 | REQ_MC_DECR, 23 | REQ_MC_QUIT, 24 | UNKNOWN 25 | }; 26 | 27 | enum memcacheCommand getMemcacheCommand(void *data); 28 | struct array *getMemcacheKeys(void *data); 29 | wstr getMemcacheKey(void *data); 30 | uint64_t getMemcacheCas(void *data); 31 | uint64_t getMemcacheNum(void *data); 32 | uint64_t getMemcacheValLen(void *data); 33 | uint64_t getMemcacheFlag(void *data); 34 | struct array *getMemcacheVal(void *data); 35 | int sendMemcacheResponse(struct conn *c, struct slice *s); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/protocol/protocol.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | -------------------------------------------------------------------------------- /src/protocol/protocol.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef WHEATSERVER_PROTOCOL_H 6 | #define WHEATSERVER_PROTOCOL_H 7 | 8 | #include "../wheatserver.h" 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/protocol/redis/proto_redis.h: -------------------------------------------------------------------------------- 1 | // Redis protocol parse implemetation 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_PROTOCOL_REDIS_PROTO_REDIS_H 8 | #define WHEATSERVER_PROTOCOL_REDIS_PROTO_REDIS_H 9 | 10 | // Protocol Redis API 11 | struct slice *redisBodyNext(struct conn *c); 12 | void redisBodyStart(struct conn*c); 13 | void getRedisKey(struct conn *c, struct slice *out); 14 | void getRedisCommand(struct conn *c, struct slice *out); 15 | int getRedisArgs(struct conn *c); 16 | int isReadCommand(struct conn*); 17 | size_t getRedisKeyEndPos(struct conn *c); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/protocol/str_macro.h: -------------------------------------------------------------------------------- 1 | #ifndef WHEATSERVER_PROTOCOL_STR_MACRO_H 2 | #define WHEATSERVER_PROTOCOL_STR_MACRO_H 3 | 4 | #define str3icmp(m, c0, c1, c2) \ 5 | ((m[0] == c0 || m[0] == (c0 ^ 0x20)) && \ 6 | (m[1] == c1 || m[1] == (c1 ^ 0x20)) && \ 7 | (m[2] == c2 || m[2] == (c2 ^ 0x20))) 8 | 9 | #define str4icmp(m, c0, c1, c2, c3) \ 10 | (str3icmp(m, c0, c1, c2) && (m[3] == c3 || m[3] == (c3 ^ 0x20))) 11 | 12 | #define str5icmp(m, c0, c1, c2, c3, c4) \ 13 | (str4icmp(m, c0, c1, c2, c3) && (m[4] == c4 || m[4] == (c4 ^ 0x20))) 14 | 15 | #define str6icmp(m, c0, c1, c2, c3, c4, c5) \ 16 | (str5icmp(m, c0, c1, c2, c3, c4) && (m[5] == c5 || m[5] == (c5 ^ 0x20))) 17 | 18 | #define str7icmp(m, c0, c1, c2, c3, c4, c5, c6) \ 19 | (str6icmp(m, c0, c1, c2, c3, c4, c5) && \ 20 | (m[6] == c6 || m[6] == (c6 ^ 0x20))) 21 | 22 | #define str8icmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ 23 | (str7icmp(m, c0, c1, c2, c3, c4, c5, c6) && \ 24 | (m[7] == c7 || m[7] == (c7 ^ 0x20))) 25 | 26 | #define str9icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \ 27 | (str8icmp(m, c0, c1, c2, c3, c4, c5, c6, c7) && \ 28 | (m[8] == c8 || m[8] == (c8 ^ 0x20))) 29 | 30 | #define str10icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9) \ 31 | (str9icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) && \ 32 | (m[9] == c9 || m[9] == (c9 ^ 0x20))) 33 | 34 | #define str11icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) \ 35 | (str10icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9) && \ 36 | (m[10] == c10 || m[10] == (c10 ^ 0x20))) 37 | 38 | #define str12icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) \ 39 | (str11icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) && \ 40 | (m[11] == c11 || m[11] == (c11 ^ 0x20))) 41 | 42 | #define str13icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) \ 43 | (str12icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) && \ 44 | (m[12] == c12 || m[12] == (c12 ^ 0x20))) 45 | 46 | #define str14icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) \ 47 | (str13icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) && \ 48 | (m[13] == c13 || m[13] == (c13 ^ 0x20))) 49 | 50 | #define str15icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14) \ 51 | (str14icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) && \ 52 | (m[14] == c14 || m[14] == (c14 ^ 0x20))) 53 | 54 | #define str16icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15) \ 55 | (str15icmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14) && \ 56 | (m[15] == c15 || m[15] == (c15 ^ 0x20))) 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/register.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuyuyu101/wheatserver/25dfb895f1d18ad7b7727aae24f0e65be4708c27/src/register.c -------------------------------------------------------------------------------- /src/setproctitle.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | /* ========================================================================== 6 | * setproctitle.c - Linux/Darwin setproctitle. 7 | * -------------------------------------------------------------------------- 8 | * Copyright (C) 2010 William Ahern 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a 11 | * copy of this software and associated documentation files (the 12 | * "Software"), to deal in the Software without restriction, including 13 | * without limitation the rights to use, copy, modify, merge, publish, 14 | * distribute, sublicense, and/or sell copies of the Software, and to permit 15 | * persons to whom the Software is furnished to do so, subject to the 16 | * following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included 19 | * in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 22 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 24 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 25 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 26 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 27 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | * ========================================================================== 29 | */ 30 | #ifndef _GNU_SOURCE 31 | #define _GNU_SOURCE 32 | #endif 33 | 34 | #include /* NULL size_t */ 35 | #include /* va_list va_start va_end */ 36 | #include /* malloc(3) setenv(3) clearenv(3) setproctitle(3) getprogname(3) */ 37 | #include /* vsnprintf(3) snprintf(3) */ 38 | 39 | #include /* strlen(3) strchr(3) strdup(3) memset(3) memcpy(3) */ 40 | 41 | #include /* errno program_invocation_name program_invocation_short_name */ 42 | 43 | #if !defined(HAVE_SETPROCTITLE) 44 | #define HAVE_SETPROCTITLE (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__) 45 | #endif 46 | 47 | 48 | #if !HAVE_SETPROCTITLE 49 | #if (defined __linux || defined __APPLE__) && defined __GNUC__ 50 | 51 | 52 | static struct { 53 | /* original value */ 54 | const char *arg0; 55 | 56 | /* title space available */ 57 | char *base, *end; 58 | 59 | /* pointer to original nul character within base */ 60 | char *nul; 61 | 62 | _Bool reset; 63 | int error; 64 | } SPT; 65 | 66 | 67 | #ifndef SPT_MIN 68 | #define SPT_MIN(a, b) (((a) < (b))? (a) : (b)) 69 | #endif 70 | 71 | static inline size_t spt_min(size_t a, size_t b) { 72 | return SPT_MIN(a, b); 73 | } /* spt_min() */ 74 | 75 | 76 | /* 77 | * For discussion on the portability of the various methods, see 78 | * http://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html 79 | */ 80 | static int spt_clearenv(void) { 81 | #if __GLIBC__ 82 | clearenv(); 83 | 84 | return 0; 85 | #else 86 | extern char **environ; 87 | static char **tmp; 88 | 89 | if (!(tmp = malloc(sizeof *tmp))) 90 | return errno; 91 | 92 | tmp[0] = NULL; 93 | environ = tmp; 94 | 95 | return 0; 96 | #endif 97 | } /* spt_clearenv() */ 98 | 99 | 100 | static int spt_copyenv(char *oldenv[]) { 101 | extern char **environ; 102 | char *eq; 103 | int i, error; 104 | 105 | if (environ != oldenv) 106 | return 0; 107 | 108 | if ((error = spt_clearenv())) 109 | goto error; 110 | 111 | for (i = 0; oldenv[i]; i++) { 112 | if (!(eq = strchr(oldenv[i], '='))) 113 | continue; 114 | 115 | *eq = '\0'; 116 | error = (0 != setenv(oldenv[i], eq + 1, 1))? errno : 0; 117 | *eq = '='; 118 | 119 | if (error) 120 | goto error; 121 | } 122 | 123 | return 0; 124 | error: 125 | environ = oldenv; 126 | 127 | return error; 128 | } /* spt_copyenv() */ 129 | 130 | 131 | static int spt_copyargs(int argc, char *argv[]) { 132 | char *tmp; 133 | int i; 134 | 135 | for (i = 1; i < argc || (i >= argc && argv[i]); i++) { 136 | if (!argv[i]) 137 | continue; 138 | 139 | if (!(tmp = strdup(argv[i]))) 140 | return errno; 141 | 142 | argv[i] = tmp; 143 | } 144 | 145 | return 0; 146 | } /* spt_copyargs() */ 147 | 148 | 149 | void spt_init(int argc, char *argv[], char *envp[]) __attribute__((constructor)); 150 | 151 | void spt_init(int argc, char *argv[], char *envp[]) { 152 | char *base, *end, *nul, *tmp; 153 | int i, error; 154 | 155 | if (!(base = argv[0])) 156 | return; 157 | 158 | nul = &base[strlen(base)]; 159 | end = nul + 1; 160 | 161 | for (i = 0; i < argc || (i >= argc && argv[i]); i++) { 162 | if (!argv[i] || argv[i] < end) 163 | continue; 164 | 165 | end = argv[i] + strlen(argv[i]) + 1; 166 | } 167 | 168 | for (i = 0; envp[i]; i++) { 169 | if (envp[i] < end) 170 | continue; 171 | 172 | end = envp[i] + strlen(envp[i]) + 1; 173 | } 174 | 175 | if (!(SPT.arg0 = strdup(argv[0]))) 176 | goto syerr; 177 | 178 | #if __GLIBC__ 179 | if (!(tmp = strdup(program_invocation_name))) 180 | goto syerr; 181 | 182 | program_invocation_name = tmp; 183 | 184 | if (!(tmp = strdup(program_invocation_short_name))) 185 | goto syerr; 186 | 187 | program_invocation_short_name = tmp; 188 | #elif __APPLE__ 189 | if (!(tmp = strdup(getprogname()))) 190 | goto syerr; 191 | 192 | setprogname(tmp); 193 | #endif 194 | 195 | 196 | if ((error = spt_copyenv(envp))) 197 | goto error; 198 | 199 | if ((error = spt_copyargs(argc, argv))) 200 | goto error; 201 | 202 | SPT.nul = nul; 203 | SPT.base = base; 204 | SPT.end = end; 205 | 206 | return; 207 | syerr: 208 | error = errno; 209 | error: 210 | SPT.error = error; 211 | } /* spt_init() */ 212 | 213 | 214 | #ifndef SPT_MAXTITLE 215 | #define SPT_MAXTITLE 255 216 | #endif 217 | 218 | void setproctitle(const char *fmt, ...) { 219 | char buf[SPT_MAXTITLE + 1]; /* use buffer in case argv[0] is passed */ 220 | va_list ap; 221 | char *nul; 222 | int len, error; 223 | 224 | if (!SPT.base) 225 | return; 226 | 227 | if (fmt) { 228 | va_start(ap, fmt); 229 | len = vsnprintf(buf, sizeof buf, fmt, ap); 230 | va_end(ap); 231 | } else { 232 | len = snprintf(buf, sizeof buf, "%s", SPT.arg0); 233 | } 234 | 235 | if (len <= 0) 236 | { error = errno; goto error; } 237 | 238 | if (!SPT.reset) { 239 | memset(SPT.base, 0, SPT.end - SPT.base); 240 | SPT.reset = 1; 241 | } else { 242 | memset(SPT.base, 0, spt_min(sizeof buf, SPT.end - SPT.base)); 243 | } 244 | 245 | len = spt_min(len, spt_min(sizeof buf, SPT.end - SPT.base) - 1); 246 | memcpy(SPT.base, buf, len); 247 | nul = &SPT.base[len]; 248 | 249 | if (nul < SPT.nul) { 250 | *SPT.nul = '.'; 251 | } else if (nul == SPT.nul && &nul[1] < SPT.end) { 252 | *SPT.nul = ' '; 253 | *++nul = '\0'; 254 | } 255 | 256 | return; 257 | error: 258 | SPT.error = error; 259 | } /* setproctitle() */ 260 | 261 | 262 | #endif /* __linux || __APPLE__ */ 263 | #endif /* !HAVE_SETPROCTITLE */ 264 | -------------------------------------------------------------------------------- /src/sig.c: -------------------------------------------------------------------------------- 1 | // Signal handler module 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include "wheatserver.h" 8 | 9 | #define MASTER_SIGNAL_COUNT 10 10 | #define WORKER_SIGNAL_COUNT 11 11 | static int signals[11] = {SIGHUP, SIGQUIT, SIGINT, SIGTERM, 12 | SIGTTIN, SIGTTOU, SIGUSR1, SIGUSR2, SIGWINCH, SIGCHLD, 13 | SIGSEGV 14 | }; 15 | 16 | /* int list */ 17 | void *dupInt(void *ptr) 18 | { 19 | int *new_ptr = wmalloc(sizeof(int)); 20 | *new_ptr = *(int *)ptr; 21 | return new_ptr; 22 | } 23 | 24 | void freeInt(void *ptr) 25 | { 26 | wfree(ptr); 27 | } 28 | 29 | int matchInt(void *ptr, void *key) 30 | { 31 | return (*(int *)ptr == *(int *)key); 32 | } 33 | 34 | static struct list *createIntList() 35 | { 36 | struct list *l = createList(); 37 | listSetDup(l, dupInt); 38 | listSetFree(l, freeInt); 39 | listSetMatch(l, matchInt); 40 | return l; 41 | } 42 | 43 | /* Signal Process */ 44 | 45 | void initWorkerSignals() 46 | { 47 | struct sigaction act, oact; 48 | int i; 49 | 50 | for (i = 0; i < WORKER_SIGNAL_COUNT; i++) { 51 | act.sa_flags = 0; 52 | if (signals[i] == SIGQUIT) 53 | act.sa_handler = handleWorkerQuit; 54 | else if (signals[i] == SIGTERM || signals[i] == SIGINT) 55 | act.sa_handler = handleWorkerAbort; 56 | else if (signals[i] == SIGUSR1) 57 | act.sa_handler = handleWorkerUsr1; 58 | else if (signals[i] == SIGSEGV) { 59 | act.sa_flags = (int)SA_RESETHAND; 60 | act.sa_handler = handleSegv; 61 | } 62 | else 63 | act.sa_handler = SIG_DFL; 64 | sigemptyset(&act.sa_mask); 65 | if (sigaction(signals[i], &act, &oact) != 0) { 66 | wheatLog(WHEAT_NOTICE, "sigaction error on %d : %s", signals[i], strerror(errno)); 67 | } 68 | } 69 | } 70 | 71 | void initMasterSignals() 72 | { 73 | Server.signal_queue = createIntList(); 74 | struct sigaction act, oact; 75 | 76 | act.sa_handler = signalProc; 77 | sigemptyset(&act.sa_mask); 78 | act.sa_flags = 0; 79 | 80 | int i; 81 | for (i = 0; i < MASTER_SIGNAL_COUNT; i++) { 82 | if (sigaction(signals[i], &act, &oact) != 0) { 83 | wheatLog(WHEAT_WARNING, "sigaction error on %d : %s", signals[i], strerror(errno)); 84 | } 85 | } 86 | 87 | act.sa_handler = handleSegv; 88 | sigemptyset(&act.sa_mask); 89 | act.sa_flags = (int)SA_RESETHAND; 90 | if (sigaction(signals[10], &act, &oact) != 0) { 91 | wheatLog(WHEAT_WARNING, "sigaction error on %d : %s", signals[i], strerror(errno)); 92 | } 93 | } 94 | 95 | void signalGenericHandle(int sig) 96 | { 97 | if (sig == SIGCHLD) 98 | handleChld(); 99 | else if (sig == SIGHUP) 100 | handleHup(); 101 | else if (sig == SIGQUIT) 102 | handleQuit(); 103 | else if (sig == SIGINT) 104 | handleInt(); 105 | else if (sig == SIGTERM) 106 | handleTerm(); 107 | else if (sig == SIGTTIN) 108 | handleTtin(); 109 | else if (sig == SIGTTOU) 110 | handleTtou(); 111 | else if (sig == SIGUSR1) 112 | handleUsr1(); 113 | else if (sig == SIGUSR2) 114 | handleUsr2(); 115 | else if (sig == SIGWINCH) 116 | handleWinch(); 117 | } 118 | 119 | void signalProc(int signal) 120 | { 121 | if (listLength(Server.signal_queue) < 5) { 122 | if (signal != SIGCHLD) 123 | appendToListTail(Server.signal_queue, &signal); 124 | wakeUp(); 125 | } 126 | } 127 | 128 | /* ========== Master Singal Handler ========== */ 129 | 130 | void handleChld() 131 | { 132 | wakeUp(); 133 | } 134 | 135 | /* HUP handling: 136 | * Reload configuration 137 | * Start the new worker processes with a new configuration 138 | * Gracefully shutdown the old worker processes */ 139 | void handleHup() 140 | { 141 | wheatLog(WHEAT_NOTICE, "Hang up: %s", Server.master_name); 142 | reload(); 143 | } 144 | 145 | void handleQuit() 146 | { 147 | wheatLog(WHEAT_WARNING, "Signal sigquit"); 148 | halt(1); 149 | } 150 | 151 | void handleInt() 152 | { 153 | wheatLog(WHEAT_WARNING, "Signal int"); 154 | halt(0); 155 | } 156 | 157 | void handleTerm() 158 | { 159 | wheatLog(WHEAT_WARNING, "Signal term"); 160 | halt(0); 161 | } 162 | 163 | void handleTtin() 164 | { 165 | Server.worker_number++; 166 | adjustWorkerNumber(); 167 | } 168 | 169 | void handleTtou() 170 | { 171 | Server.worker_number--; 172 | adjustWorkerNumber(); 173 | } 174 | 175 | void handleUsr1() 176 | { 177 | wheatLog(WHEAT_NOTICE, "Signal usr1: %s", Server.master_name); 178 | killAllWorkers(SIGUSR1); 179 | logStat(); 180 | } 181 | 182 | /* SIGUSR2 handling. Creates a new master/worker set as a 183 | * slave of the current * master without affecting old workers. 184 | * Use this to do live deployment with the ability to backout a change. */ 185 | void handleUsr2() 186 | { 187 | wheatLog(WHEAT_NOTICE, "Signal usr2: %s reexec", Server.master_name); 188 | reexec(); 189 | } 190 | 191 | void handleWinch() 192 | { 193 | if (getppid() == 1 || getpgrp() != getpid()) { 194 | wheatLog(WHEAT_NOTICE, "Signal winch: graceful stop of workers"); 195 | Server.worker_number = 0; 196 | killAllWorkers(SIGQUIT); 197 | } else { 198 | wheatLog(WHEAT_NOTICE, "Signal winch ignored, not daemonize"); 199 | } 200 | } 201 | 202 | /* ========== Worker Singal Handler ========== */ 203 | void handleWorkerUsr1(int sig) 204 | { 205 | // worker process refresh_time is used to control when to send statistic 206 | // packet, set to zero means to force worker to send 207 | WorkerProcess->refresh_time = 0; 208 | return ; 209 | } 210 | 211 | void handleWorkerAbort(int sig) 212 | { 213 | WorkerProcess->alive = 0; 214 | halt(1); 215 | } 216 | 217 | void handleWorkerQuit(int sig) 218 | { 219 | WorkerProcess->alive = 0; 220 | } 221 | 222 | void handleSegv() 223 | { 224 | wheat_stacktrace(1); 225 | wheatLogRaw(WHEAT_NOTICE, "Signal SEGV received, core dump"); 226 | raise(SIGSEGV); 227 | } 228 | -------------------------------------------------------------------------------- /src/sig.h: -------------------------------------------------------------------------------- 1 | // Signal handler module 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSEVER_SIGNAL_H 8 | #define WHEATSEVER_SIGNAL_H 9 | 10 | // We change SIGHUP, SIGQUIT, SIGINT, SIGTERM, SIGTTIN, SIGTTOU, SIGUSR1, 11 | // SIGUSR2, SIGWINCH, SIGCHLD, SIGSEGV handler function to customize use. 12 | void initMasterSignals(); 13 | void initWorkerSignals(); 14 | 15 | void signalProc(int signal); 16 | void signalGenericHandle(int sig); 17 | 18 | /* Master Handle Signal Function */ 19 | void handleChld(); 20 | void handleHup(); 21 | void handleQuit(); 22 | void handleInt(); 23 | void handleTerm(); 24 | void handleTtin(); 25 | void handleTtou(); 26 | void handleUsr1(); 27 | void handleUsr2(); 28 | void handleWinch(); 29 | void handleSegv(); 30 | 31 | /* Worker Handle Signal Function */ 32 | void handleWorkerUsr1(int); 33 | void handleWorkerAbort(int); 34 | void handleWorkerQuit(int); 35 | void handleWorkerAlrm(int); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/slab.c: -------------------------------------------------------------------------------- 1 | // Implementation of memory management base on Wheatserver's features 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "debug.h" 12 | 13 | #define WHEAT_SLAB_SIZE (4<<10) 14 | #define WHEAT_ALIGN_BYTES (sizeof(void *)<<1) 15 | #define WHEAT_SLABCLASS_MAX_SIZE 20 16 | 17 | #define WHEAT_ALIGN(d, a) (((d) + (a - 1)) & ~(a - 1)) 18 | 19 | struct slab { 20 | uint8_t *start; 21 | struct slab *next; 22 | }; 23 | 24 | struct slabClass { 25 | uint32_t per_slab_items; 26 | uint32_t per_item_size; 27 | 28 | struct slab *free_slab; 29 | uint32_t free_item_size; 30 | uint8_t *free_slab_pos; 31 | }; 32 | 33 | struct slabcenter { 34 | struct slabClass slab_classes[WHEAT_SLABCLASS_MAX_SIZE]; 35 | uint32_t max_item_size; 36 | double factor; 37 | struct slab *slabs; 38 | struct slab *bigs; 39 | }; 40 | 41 | static size_t slabSizeToid(struct slabcenter *center, const size_t size) 42 | { 43 | int i = 0; 44 | while (size > center->slab_classes[i].per_item_size && i < WHEAT_SLABCLASS_MAX_SIZE) 45 | i++; 46 | if (i == WHEAT_SLABCLASS_MAX_SIZE) 47 | return 0; 48 | return i; 49 | } 50 | 51 | static struct slab *slabAllocSlab(struct slabcenter *center) 52 | { 53 | uint8_t *p = malloc(sizeof(struct slab)+WHEAT_SLAB_SIZE); 54 | if (!p) 55 | return NULL; 56 | struct slab *slab = (struct slab *)p; 57 | slab->start = p + sizeof(struct slab); 58 | slab->next = center->slabs; 59 | center->slabs = slab->next; 60 | return slab; 61 | } 62 | 63 | static struct slab *slabAllocBig(struct slabcenter* center, size_t size) 64 | { 65 | uint8_t *p = malloc(sizeof(struct slab)+size); 66 | if (!p) 67 | return NULL; 68 | struct slab *slab = (struct slab *)p; 69 | slab->start = p + sizeof(struct slab); 70 | slab->next = center->bigs; 71 | center->bigs = slab->next; 72 | return slab; 73 | } 74 | 75 | struct slabcenter *slabcenterCreate(const int item_max, const double factor) 76 | { 77 | ASSERT(item_max < WHEAT_SLAB_SIZE); 78 | uint32_t i = 0; 79 | uint32_t size = WHEAT_ALIGN_BYTES; 80 | 81 | struct slabcenter *c = malloc(sizeof(*c)); 82 | memset(c, 0, sizeof(*c)); 83 | c->factor = factor; 84 | c->max_item_size = item_max; 85 | while (i++ <= WHEAT_SLABCLASS_MAX_SIZE && size <= c->max_item_size) { 86 | size = WHEAT_ALIGN(size, WHEAT_ALIGN_BYTES); 87 | 88 | c->slab_classes[i].per_item_size = size; 89 | c->slab_classes[i].per_slab_items = (WHEAT_SLAB_SIZE) / size; 90 | c->slab_classes[i].free_slab = NULL; 91 | c->slab_classes[i].free_item_size = 0; 92 | size *= factor; 93 | } 94 | return c; 95 | } 96 | 97 | void slabcenterDealloc(struct slabcenter *c) 98 | { 99 | struct slab *s = c->slabs, *next; 100 | while (s != NULL) { 101 | next = s; 102 | free(s); 103 | s = next; 104 | } 105 | free(c); 106 | } 107 | 108 | void *slabAlloc(struct slabcenter *center, const size_t size) 109 | { 110 | if (size == 0) 111 | return NULL; 112 | if (size > center->max_item_size) { 113 | struct slab *big = slabAllocBig(center, size); 114 | return big->start; 115 | } 116 | const size_t id = slabSizeToid(center, size); 117 | struct slabClass *slab_class = ¢er->slab_classes[id]; 118 | uint8_t *ptr = NULL; 119 | if (slab_class->free_slab == NULL || slab_class->free_item_size == 0) { 120 | slab_class->free_slab = slabAllocSlab(center); 121 | slab_class->free_item_size = slab_class->per_slab_items; 122 | slab_class->free_slab_pos = slab_class->free_slab->start; 123 | } 124 | ptr = slab_class->free_slab_pos; 125 | slab_class->free_slab_pos += slab_class->per_item_size; 126 | return ptr; 127 | } 128 | -------------------------------------------------------------------------------- /src/slab.h: -------------------------------------------------------------------------------- 1 | // Implementation of memory management base on Wheatserver's features 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_SLAB_H 8 | #define WHEATSERVER_SLAB_H 9 | 10 | struct slabcenter; 11 | 12 | // Note: 13 | // Learn memory pool from Nginx and simplify it. 14 | // It can reach rapid allocation and needn't free objects. Based on 15 | // Wheatserver's request-response model, alloc a big buffer and 16 | // distribute them, free all finally. 17 | 18 | struct slabcenter *slabcenterCreate(const int item_max, const double factor); 19 | void slabcenterDealloc(struct slabcenter *c); 20 | 21 | void *slabAlloc(struct slabcenter *center, const size_t size); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/slice.c: -------------------------------------------------------------------------------- 1 | // A string implementation of no payload 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "slice.h" 12 | #include "memalloc.h" 13 | #include "debug.h" 14 | 15 | struct slice *sliceCreate(uint8_t *data, size_t len) 16 | { 17 | struct slice *b = wmalloc(sizeof(*b)); 18 | if (data == NULL) 19 | b->len = 0; 20 | else 21 | b->len = len; 22 | b->data = data; 23 | return b; 24 | } 25 | 26 | void sliceTo(struct slice *s, uint8_t *data, size_t len) 27 | { 28 | if (data == NULL) 29 | return ; 30 | s->len = len; 31 | s->data = data; 32 | } 33 | 34 | void sliceFree(struct slice *b) 35 | { 36 | wfree(b); 37 | } 38 | 39 | void sliceClear(struct slice *b) 40 | { 41 | b->len = 0; 42 | b->data = NULL; 43 | } 44 | 45 | void sliceRemvoePrefix(struct slice *b, size_t prefix) 46 | { 47 | assert(prefix < b->len); 48 | b->data += prefix; 49 | b->len -= prefix; 50 | } 51 | 52 | int sliceStartWith(struct slice *b, struct slice *s) 53 | { 54 | return ((b->len >= s->len) && 55 | (memcmp(b->data, s->data, s->len) == 0)); 56 | } 57 | 58 | int sliceCompare(struct slice *b, struct slice *s) 59 | { 60 | const size_t min_len = (b->len < s->len) ? b->len : s->len; 61 | int r = memcmp(b->data, s->data, min_len); 62 | if (r == 0) { 63 | if (b->len < s->len) 64 | r = -1; 65 | else if 66 | (b->len > s->len) r = 1; 67 | } 68 | return r; 69 | } 70 | 71 | #ifdef SLICE_TEST_MAIN 72 | #include 73 | #include "test_help.h" 74 | 75 | int main(int argc, const char *argv[]) 76 | { 77 | { 78 | struct slice *s1 = sliceCreate((uint8_t *)"abcdefg", 7); 79 | struct slice *s2 = sliceCreate((uint8_t *)"abcd", 4); 80 | test_cond((uint8_t *)"sliceStartWith", sliceStartWith(s1, s2)); 81 | sliceRemvoePrefix(s1, 2); 82 | test_cond("sliceStartWith not", !sliceStartWith(s1, s2)); 83 | sliceClear(s1); 84 | sliceTo(s1, (uint8_t *)"abcd", 4); 85 | test_cond("sliceCompare", !sliceCompare(s1, s2)); 86 | } 87 | test_report(); 88 | return 0; 89 | } 90 | #endif 91 | 92 | -------------------------------------------------------------------------------- /src/slice.h: -------------------------------------------------------------------------------- 1 | // A string implementation of no payload 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_SLICES_H 8 | #define WHEATSERVER_SLICES_H 9 | 10 | #include 11 | 12 | // Learn from LevelDB, using slice in Wheatserver everywhere(Like it very much). 13 | // 14 | // Typical case: 15 | // 1. 16 | // char buf[1024]; 17 | // struct slice slice; 18 | // sliceTo(&slice, buf, 1024); 19 | // readBulkFrom(fd, &slice); 20 | // 2. 21 | // char packet[4096]; 22 | // ... 23 | // struct slice field, value; 24 | // parsePacket(packet, &field, &value); 25 | 26 | struct slice { 27 | uint8_t *data; 28 | size_t len; 29 | }; 30 | 31 | struct slice *sliceCreate(uint8_t *data, size_t len); 32 | void sliceTo(struct slice *s, uint8_t *data, size_t len); 33 | void sliceFree(struct slice *b); 34 | void sliceClear(struct slice *b); 35 | void sliceRemvoePrefix(struct slice *b, size_t prefix); 36 | int sliceStartWith(struct slice *b, struct slice *s); 37 | int sliceCompare(struct slice *b, struct slice *s); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/stats.h: -------------------------------------------------------------------------------- 1 | // Statistic module - implementation of send statistic packet and revevant 2 | // utils 3 | // 4 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 5 | // Use of this source code is governed by a BSD-style license that can be 6 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 7 | 8 | #ifndef WHEATSERVER_STATS_H_ 9 | #define WHEATSERVER_STATS_H_ 10 | 11 | // Gather statistic example: 12 | // static struct statItem *stat_item = NULL; 13 | // static long long *stat_item_val = 0; 14 | // 15 | // void XXXXSetup() 16 | // { 17 | // stat_item = getStatItemByName("stat item"); 18 | // stat_item_val = &getStatValByName("stat item val"); 19 | // } 20 | // 21 | // void appCron() 22 | // { 23 | // getStatVal(stat_item)++; 24 | // *stat_item_val++; 25 | // 26 | // OR 27 | // getStatVal(stat_item) = some_val; 28 | // *stat_item_val = some_val; 29 | // } 30 | // 31 | // You can cache stat item on your module in order to reduce search stat item 32 | // by name every time. Via referencing `value` or statItem both are OK. 33 | // 34 | // Statistic note: 35 | // Worker process will gather changes on statistic fields and send changes 36 | // to master process every heart cron. 37 | 38 | #define ONLY_MASTER (1) 39 | 40 | #define getStatValByName(name) (getStatItemByName(name)->val) 41 | #define getStatVal(stat) ((stat)->val) 42 | 43 | enum statType { 44 | SUM_STAT, 45 | MAX_STAT, 46 | ASSIGN_STAT, 47 | }; 48 | 49 | enum statPrintFormat { 50 | RAW, 51 | MICORSECONDS_TIME, 52 | LOCAL_TIME, 53 | }; 54 | 55 | // You can define your statistic item in your module. 56 | // `name`: statistic field name, displayed in statistic report 57 | // `type`: use type below listed. You can get exact purpose from StatItems in 58 | // stats.c 59 | // 1. ASSIGN_STAT: statistic value is assigned to master aggregation 60 | // 2. SUM_STAT: statistic value is the sum of all value gathered 61 | // 3. MAX_STAT: statistic value is set the max value of all value gathered 62 | // `format`: statistic value formatted type, you can specify below listed. 63 | // 1. LOCAL_TIME 64 | // 2. MICORSECONDS_TIME 65 | // 3. RAW 66 | // `flags`: flags indicate stat item 67 | // * ONLY_MASTER: this statistic field is ignored when worker send statistic 68 | // packet to master process. In other words, this field is only used master 69 | // process. 70 | // `val`: the actual place storing message 71 | struct statItem { 72 | char *name; 73 | enum statType type; 74 | enum statPrintFormat format; 75 | int flags; 76 | long long val; 77 | }; 78 | 79 | struct masterClient; 80 | struct workerProcess; 81 | 82 | struct statItem *getStatItemByName(const char *name); 83 | void sendStatPacket(struct workerProcess *worker_process); 84 | void logStat(); 85 | void statinputCommand(struct masterClient *c); 86 | void statCommand(struct masterClient *c); 87 | void initServerStats(struct array *confs); 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/test_help.h: -------------------------------------------------------------------------------- 1 | // This is a really minimal testing framework for C. Learn from 2 | // [MinUnit](http://www.jera.com/techinfo/jtns/jtn002.html) 3 | // 4 | // Example: 5 | // 6 | // test_cond("Check if 1 == 1", 1==1) 7 | // test_cond("Check if 5 > 10", 5 > 10) 8 | // test_report() 9 | // 10 | // ---------------------------------------------------------------------------- 11 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 12 | // Use of this source code is governed by a BSD-style license that can be 13 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 14 | 15 | #ifndef WHEATSERVER_TEST_HELP_H 16 | #define WHEATSERVER_TEST_HELP_H 17 | 18 | // In Wheatserver, use this unit test framework to test base data structure 19 | // like list, dict, wstr, slice, mbuf. 20 | // You can find using example in these implementation file 21 | 22 | int __failed_tests = 0; 23 | int __test_num = 0; 24 | 25 | #define test_cond(descr,_c) do { \ 26 | __test_num++; printf("%d - %s: ", __test_num, descr); \ 27 | if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \ 28 | } while(0) 29 | 30 | #define test_report() do { \ 31 | printf("%d tests, %d passed, %d failed\n", __test_num, \ 32 | __test_num-__failed_tests, __failed_tests); \ 33 | if (__failed_tests) { \ 34 | printf("=== WARNING === We have failed tests here...\n"); \ 35 | exit(1); \ 36 | } \ 37 | } while(0) 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | // Functions used to wrap necessary call 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_UTIL_H 8 | #define WHEATSERVER_UTIL_H 9 | 10 | /* Hash Type Dict */ 11 | struct dictType; 12 | struct configuration; 13 | extern struct dictType wstrDictType; 14 | extern struct dictType sliceDictType; 15 | extern struct dictType intDictType; 16 | 17 | void nonBlockCloseOnExecPipe(int *fd0, int *fd1); 18 | 19 | int daemonize(int coredump); 20 | void createPidFile(); 21 | // set proctitle 22 | void setProctitle(const char *title); 23 | int getFileSizeAndMtime(int fd, off_t *len, time_t *m_time); 24 | int isRegFile(const char *path); 25 | int fromSameParentDir(wstr left, wstr right); 26 | void setTimer(int milliseconds); 27 | int ll2string(char *s, size_t len, long long value); 28 | int string2ll(const char *s, size_t slen, long long *value); 29 | size_t getIntLen(unsigned long i); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #define WHEATSERVER_VERSION "0.6.2" 6 | -------------------------------------------------------------------------------- /src/wheatserver.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 | 5 | #ifndef WHEATSEVER_WHEATSSERVER_H 6 | #define WHEATSEVER_WHEATSSERVER_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "array.h" 24 | #include "dict.h" 25 | #include "list.h" 26 | #include "slice.h" 27 | #include "wstr.h" 28 | 29 | #include "debug.h" 30 | #include "event.h" 31 | #include "hook.h" 32 | #include "memalloc.h" 33 | #include "net.h" 34 | #include "networking.h" 35 | #include "portable.h" 36 | #include "sig.h" 37 | #include "stats.h" 38 | #include "util.h" 39 | #include "version.h" 40 | #include "worker/worker.h" 41 | #include "protocol/protocol.h" 42 | #include "app/application.h" 43 | 44 | // Server Configuration 45 | #define WHEAT_DEFAULT_ADDR "127.0.0.1" 46 | #define WHEAT_SERVERPORT 10828 47 | #define WHEATSERVER_CONFIGLINE_MAX 1024 48 | #define WHEATSERVER_MAX_LOG_LEN 1024 49 | #define WHEATSERVER_MAX_NAMELEN 1024 50 | #define WHEATSERVER_MAX_PORT_RANGE 20480 51 | #define WHEATSERVER_PATH_LEN 1024 52 | #define WHEATSERVER_GRACEFUL_TIME 5 53 | #define WHEATSERVER_IDLE_TIME 1 54 | #define WHEATSERVER_TIMEOUT 30 55 | #define WHEAT_NOTFREE 1 56 | #define WHEATSERVER_CRON_MILLLISECONDS 100 57 | #define WHEAT_PREALLOC_CLIENT_LIMIT 10000 58 | #define WHEAT_BUFLIMIT (1024*1024*1024) 59 | #define WHEAT_ARGS_NO_LIMIT (-1) 60 | #define WHEAT_MBUF_SIZE (16*1024) 61 | #define WHEAT_PROTOCOL_DEFAULT "Http" 62 | #define WHEAT_CRON_HZ 10 63 | #define WHEAT_CLIENT_MAX 4096 64 | 65 | // Statistic Configuration 66 | #define WHEAT_STATS_PORT 10829 67 | #define WHEAT_STATS_ADDR "127.0.0.1" 68 | #define WHEAT_STAT_REFRESH 10 69 | #define WHEAT_STAT_PACKET_MAX 512 70 | #define WHEAT_DEFAULT_WORKER "SyncWorker" 71 | #define WHEAT_ASTERISK "*" 72 | #define WHEAT_PREALLOC_CLIENT 100 73 | #define WHEAT_MAX_BUFFER_SIZE (4*1024*1024) 74 | #define WHEAT_MAX_FILE_LIMIT (16*1024*1024) 75 | #define WHEAT_STR_NULL "NULL" 76 | 77 | // Command Format 78 | #define WHEAT_START_SPLIT "\r\r" 79 | 80 | // Log levels 81 | #define WHEAT_DEBUG 0 82 | #define WHEAT_VERBOSE 1 83 | #define WHEAT_NOTICE 2 84 | #define WHEAT_WARNING 4 85 | #define WHEAT_LOG_RAW 8 86 | 87 | // Config validator return values 88 | #define VALIDATE_OK 0 89 | #define VALIDATE_WRONG 1 90 | 91 | /* Using the following macro you can run code inside workerProcessCron() with 92 | * the specified period, specified in milliseconds. 93 | * The actual resolution depends on WHEAT_WOKER_HZ. */ 94 | // Learn from redis.c 95 | #define runWithPeriod(_ms_) \ 96 | if ((_ms_ <= 1000/WHEAT_CRON_HZ) || !(Server.cron_loops%((_ms_)/(1000/WHEAT_CRON_HZ)))) 97 | #define getMicroseconds(time) (time.tv_sec*1000000+time.tv_usec) 98 | 99 | 100 | // globalServer is only one for Wheatserver instance. 101 | // 102 | // `port_ranges`: A range of ports needed to be bind 103 | // `worker_type`: Worker module name 104 | // `graceful_timeout`: interval for worker process exit gracefully 105 | // `worker_timeout`: interval for worker process timeout trigger 106 | // `pipe_readfd`, `pipe_writefd`: used to wake up master process, it's relevant 107 | // for signal handler 108 | // `workers`: alive worker process list 109 | // `master_clients`: clients connect to master process(statistic listen) 110 | // `signal_queue`: signal waiting queue 111 | // `modules`: aggregation of modules including worker, protocol, application 112 | // `confs`: the collection of configuration items in modules 113 | // `commands`: the collection of command items in modules 114 | // `stats`: the collection of statistic items in modules 115 | struct globalServer { 116 | char *bind_addr; 117 | int port_range_start; 118 | int port_range_end; 119 | int worker_number; 120 | char *worker_type; 121 | char configfile_path[WHEATSERVER_PATH_LEN]; 122 | struct hookCenter *hook_center; 123 | int graceful_timeout; 124 | int daemon; 125 | char *pidfile; 126 | int max_buffer_size; 127 | int worker_timeout; 128 | 129 | char *stat_addr; 130 | int stat_port; 131 | int stat_refresh_seconds; 132 | char *stat_file; 133 | 134 | // status 135 | char master_name[WHEATSERVER_MAX_NAMELEN]; 136 | int ipfd[WHEATSERVER_MAX_PORT_RANGE]; 137 | struct evcenter *master_center; 138 | int stat_fd; 139 | struct timeval cron_time; 140 | long long cron_loops; 141 | pid_t pid; 142 | pid_t relaunch_pid; 143 | int pipe_readfd; 144 | int pipe_writefd; 145 | size_t mbuf_size; 146 | struct list *workers; 147 | struct list *master_clients; 148 | struct list *signal_queue; 149 | 150 | struct list *modules; 151 | struct list *confs; 152 | struct array *commands; 153 | struct array *stats; 154 | 155 | // log 156 | char *logfile; 157 | int verbose; 158 | 159 | // error 160 | char neterr[NET_ERR_LEN]; 161 | }; 162 | 163 | struct masterClient { 164 | int fd; 165 | wstr request_buf; 166 | wstr response_buf; 167 | int argc; 168 | wstr *argv; 169 | }; 170 | 171 | struct enumIdName { 172 | int id; 173 | char *name; 174 | }; 175 | 176 | enum printFormat { 177 | INT_FORMAT, 178 | STRING_FORMAT, 179 | ENUM_FORMAT, 180 | BOOL_FORMAT, 181 | LIST_FORMAT 182 | }; 183 | 184 | // `target` is the actual field to store config value. You can use int, pointer 185 | // or enum type as your config type. Directly store right field. 186 | struct configuration { 187 | char *name; 188 | int args; // -1 means no limit on args 189 | int (*validator)(struct configuration *conf, const char *key, const char *value); 190 | union { 191 | int val; 192 | void *ptr; 193 | struct enumIdName *enum_ptr; 194 | } target; 195 | void *helper; // using in validator, indicating target attribute 196 | enum printFormat format; 197 | }; 198 | 199 | struct command { 200 | char *command_name; 201 | int args; 202 | void (*command_func)(struct masterClient *); 203 | char *description; 204 | }; 205 | 206 | enum moduleType { 207 | APP, 208 | PROTOCOL, 209 | WORKER, 210 | }; 211 | 212 | struct moduleAttr { 213 | char *name; 214 | enum moduleType type; 215 | union { 216 | struct app *app; 217 | struct worker *worker; 218 | struct protocol *protocol; 219 | void *p; 220 | } module; 221 | struct statItem *stats; 222 | size_t stat_size; 223 | struct configuration *confs; 224 | size_t conf_size; 225 | struct command *commands; 226 | size_t command_size; 227 | }; 228 | 229 | struct workerProcess; 230 | 231 | extern struct globalServer Server; 232 | extern struct moduleAttr *ModuleTable[]; 233 | 234 | void initServer(); 235 | 236 | // ============ Worker Process Restart ============= 237 | void reload(); 238 | void reexec(); 239 | 240 | // ============ Worker Process Management ========== 241 | void adjustWorkerNumber(); 242 | void murderIdleWorkers(); 243 | void killWorker(struct workerProcess *worker, int sig); 244 | void killAllWorkers(int sig); 245 | void spawnWorker(char *worker_name); 246 | void spawnFakeWorker(void (*func)(void *), void *data); 247 | void wakeUp(); 248 | // graceful means whether to wait worker 249 | // conncction completion 250 | void stopWorkers(int graceful); 251 | void halt(int exitcode); 252 | 253 | // ============== Configuration ==================== 254 | void loadConfigFile(const char *filename, char *options, int test); 255 | void fillServerConfig(); 256 | void printServerConfig(); 257 | struct configuration *getConfiguration(const char *name); 258 | void configCommand(struct masterClient *); 259 | 260 | // ============== Configuration validator ========== 261 | void initServerConfs(struct list *confs); 262 | int stringValidator(struct configuration *conf, const char *key, const char *val); 263 | int unsignedIntValidator(struct configuration *conf, const char *key, const char *val); 264 | int enumValidator(struct configuration *conf, const char *key, const char *val); 265 | int boolValidator(struct configuration *conf, const char *key, const char *val); 266 | int listValidator(struct configuration *conf, const char *key, const char *val); 267 | 268 | // =================== Log ========================= 269 | void wheatLogRaw(int level, const char *msg); 270 | void wheatLog(int level, const char *fmt, ...); 271 | 272 | // ============ Master Client Operation ============ 273 | struct masterClient *createMasterClient(int fd); 274 | void freeMasterClient(struct masterClient *c); 275 | void logRedirect(); 276 | 277 | 278 | #define WHEAT_WRONG -1 279 | #define WHEAT_OK 0 280 | 281 | // ============== Module Get Operation ============= 282 | #define getProtocol(m) (m->module.protocol) 283 | #define getWorker(m) (m->module.worker) 284 | #define getApp(m) (m->module.app) 285 | 286 | struct moduleAttr *getModule(enum moduleType type, const char *name); 287 | const char *getModuleName(enum moduleType type, void *t); 288 | void getAppsByProtocol(struct array *apps, struct protocol *p); 289 | 290 | static inline struct worker *spotWorker(const char *name) 291 | { 292 | struct moduleAttr *_module_attr = getModule(WORKER, name); 293 | return _module_attr->module.worker; 294 | } 295 | 296 | static inline struct protocol *spotProtocol(const char *name) 297 | { 298 | struct moduleAttr *_module_attr = getModule(PROTOCOL, name); 299 | return _module_attr->module.protocol; 300 | } 301 | 302 | static inline struct app *spotApp(const char *name) 303 | { 304 | struct moduleAttr *_module_attr = getModule(APP, name); 305 | return _module_attr->module.app; 306 | } 307 | 308 | #endif 309 | -------------------------------------------------------------------------------- /src/worker/mbuf.c: -------------------------------------------------------------------------------- 1 | // mbuf structure implemetation 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../debug.h" 13 | #include "../slice.h" 14 | #include "mbuf.h" 15 | #include "../memalloc.h" 16 | 17 | #define WHEAT_MBUF_MAGIC 0x19920828 18 | 19 | // msghdr as a management unit to manage multi mbuf. 20 | // 21 | // mbuf_size 22 | // <------> 23 | // +------+ +------+ +------+ +------+ +------+ 24 | // | mbuf |->| mbuf |->| mbuf |->| mbuf |->| mbuf | 25 | // +------+ +------+ +------+ +------+ +------+ 26 | // | | | 27 | // | last_read | 28 | // protected last_write 29 | 30 | struct msghdr { 31 | struct mbuf *last_write; 32 | struct mbuf *protected; 33 | struct mbuf *last_read; 34 | size_t mbuf_len; 35 | size_t mbuf_size; 36 | uint8_t is_set_writted_after_put; 37 | uint8_t is_set_readed_after_read; 38 | }; 39 | 40 | // mbuf header is at the tail end of the mbuf. This enables us to catch 41 | // buffer overrun early by asserting on the magic value during get or 42 | // put operations 43 | // 44 | // <------------- mbuf_chunk_size -------------> 45 | // +-------------------------------------------+ 46 | // | mbuf data | mbuf header | 47 | // | (mbuf_offset) | (struct mbuf) | 48 | // +-------------------------------------------+ 49 | // ^ ^ ^ ^^ 50 | // | | | || 51 | // \ | | |\ 52 | // mbuf->start \ | | mbuf->end (one byte past valid bound) 53 | // mbuf->protect_pos\ 54 | // \ mbuf 55 | // mbuf->write_pos (one byte past valid byte) 56 | 57 | struct mbuf { 58 | uint32_t magic; 59 | struct mbuf *next; 60 | uint8_t *read_pos; 61 | uint8_t *write_pos; 62 | uint8_t *start; 63 | uint8_t *end; 64 | }; 65 | 66 | 67 | static struct mbuf *mbufGet(size_t mbuf_size) 68 | { 69 | struct mbuf *mbuf; 70 | // extra one is left as mbuf->end 71 | uint8_t *m = wmalloc(sizeof(*mbuf)+mbuf_size+1); 72 | if (m == NULL) 73 | return NULL; 74 | mbuf = (struct mbuf *)(m + mbuf_size + 1); 75 | mbuf->end = m + mbuf_size; 76 | mbuf->read_pos = mbuf->write_pos = mbuf->start = m; 77 | mbuf->magic = WHEAT_MBUF_MAGIC; 78 | mbuf->next = NULL; 79 | return mbuf; 80 | } 81 | 82 | static void mbufFree(struct mbuf *mbuf, size_t mbuf_size) 83 | { 84 | uint8_t *m = (uint8_t *)mbuf - mbuf_size - 1; 85 | wfree(m); 86 | } 87 | 88 | struct msghdr *msgCreate(size_t mbuf_size) 89 | { 90 | struct mbuf *mbuf = mbufGet(mbuf_size); 91 | if (mbuf == NULL) 92 | return NULL; 93 | struct msghdr *hdr = wmalloc(sizeof(*hdr)); 94 | if (hdr == NULL) { 95 | mbufFree(mbuf, mbuf_size); 96 | return NULL; 97 | } 98 | hdr->last_read = hdr->last_write = hdr->protected = mbuf; 99 | hdr->mbuf_len = 1; 100 | hdr->mbuf_size = mbuf_size; 101 | hdr->is_set_writted_after_put = hdr->is_set_readed_after_read = 1; 102 | return hdr; 103 | } 104 | 105 | void msgClean(struct msghdr *hdr) 106 | { 107 | struct mbuf *next, *curr = hdr->protected; 108 | size_t mbuf_size = hdr->mbuf_size; 109 | while (curr && curr != hdr->last_read) { 110 | next = curr->next; 111 | mbufFree(curr, mbuf_size); 112 | curr = next; 113 | hdr->mbuf_len--; 114 | } 115 | hdr->protected = curr; 116 | } 117 | 118 | void msgRead(struct msghdr *hdr, struct slice *s) 119 | { 120 | assert(hdr && s); 121 | assert(hdr->is_set_readed_after_read == 1); 122 | struct mbuf *mbuf = hdr->last_read; 123 | assert(mbuf->magic == WHEAT_MBUF_MAGIC); 124 | // If hdr->last_read is read over but next mbuf exists 125 | if (mbuf->read_pos == mbuf->write_pos && mbuf->write_pos == mbuf->end && 126 | mbuf->next != NULL) { 127 | mbuf = mbuf->next; 128 | } 129 | sliceTo(s, mbuf->read_pos, mbuf->write_pos - mbuf->read_pos); 130 | hdr->last_read = mbuf; 131 | hdr->is_set_readed_after_read = 0; 132 | } 133 | 134 | int msgPut(struct msghdr *hdr, struct slice *s) 135 | { 136 | assert(hdr && s); 137 | assert(hdr->is_set_writted_after_put == 1); 138 | 139 | struct mbuf *mbuf = hdr->last_write; 140 | assert(mbuf->magic == WHEAT_MBUF_MAGIC); 141 | if (mbuf->write_pos == mbuf->end) { 142 | struct mbuf *old_buf = mbuf; 143 | mbuf = mbufGet(hdr->mbuf_size); 144 | if (mbuf == NULL) 145 | return -1; 146 | old_buf->next = mbuf; 147 | hdr->mbuf_len++; 148 | hdr->last_write = mbuf; 149 | } 150 | sliceTo(s, mbuf->write_pos, mbuf->end - mbuf->write_pos); 151 | hdr->is_set_writted_after_put = 0; 152 | return 0; 153 | } 154 | 155 | void msgFree(struct msghdr *hdr) 156 | { 157 | struct mbuf *next, *curr = hdr->protected; 158 | while (curr != NULL) { 159 | next = curr->next; 160 | mbufFree(curr, hdr->mbuf_size); 161 | curr = next; 162 | hdr->mbuf_len--; 163 | } 164 | wfree(hdr); 165 | } 166 | 167 | void msgSetReaded(struct msghdr *hdr, size_t len) 168 | { 169 | assert(hdr->last_read->magic == WHEAT_MBUF_MAGIC); 170 | assert(len <= hdr->last_read->write_pos - hdr->last_read->read_pos); 171 | assert(hdr->is_set_readed_after_read == 0); 172 | hdr->last_read->read_pos += len; 173 | hdr->is_set_readed_after_read = 1; 174 | } 175 | 176 | void msgSetWritted(struct msghdr *hdr, size_t len) 177 | { 178 | assert(hdr->last_write->magic == WHEAT_MBUF_MAGIC); 179 | assert(len <= hdr->last_write->end - hdr->last_write->write_pos); 180 | assert(hdr->is_set_writted_after_put == 0); 181 | hdr->last_write->write_pos += len; 182 | hdr->is_set_writted_after_put = 1; 183 | } 184 | 185 | size_t msgGetSize(struct msghdr *hdr) 186 | { 187 | return hdr->mbuf_len * hdr->mbuf_size; 188 | } 189 | 190 | int msgCanRead(struct msghdr *hdr) 191 | { 192 | struct mbuf *buf = hdr->last_read; 193 | assert(buf->magic == WHEAT_MBUF_MAGIC); 194 | return (buf->read_pos != buf->write_pos) || 195 | (buf->next != NULL && buf->next->write_pos != buf->next->start); 196 | } 197 | 198 | #ifdef MBUF_TEST_MAIN 199 | #include 200 | #include "../test_help.h" 201 | 202 | int main(int argc, const char *argv[]) 203 | { 204 | { 205 | size_t mbuf_size = 512; 206 | struct msghdr *hdr = msgCreate(mbuf_size); 207 | struct slice slice; 208 | int ret = msgPut(hdr, &slice); 209 | test_cond("msg put", ret == 0); 210 | test_cond("msg put", slice.len == mbuf_size); 211 | char buf[255]; 212 | int i = 0; 213 | for (; i < 255; ++i) 214 | buf[i] = 'a'; 215 | memcpy(slice.data, buf, 255); 216 | msgSetWritted(hdr, 255); 217 | msgRead(hdr, &slice); 218 | test_cond("msg get", slice.len == 255); 219 | msgSetReaded(hdr, 255); 220 | msgRead(hdr, &slice); 221 | test_cond("msg get", slice.len == 0); 222 | msgSetReaded(hdr, slice.len); 223 | ret = msgPut(hdr, &slice); 224 | test_cond("msg put", ret == 0 && slice.len == mbuf_size-255); 225 | msgSetWritted(hdr, mbuf_size-255); 226 | msgRead(hdr, &slice); 227 | msgSetReaded(hdr, slice.len); 228 | test_cond("msg get", ret == 0 && slice.len == mbuf_size-255); 229 | ret = msgPut(hdr, &slice); 230 | test_cond("msg put", ret == 0 && slice.len == mbuf_size); 231 | msgSetWritted(hdr, 255); 232 | msgRead(hdr, &slice); 233 | msgSetReaded(hdr, slice.len); 234 | test_cond("msg get", ret == 0 && slice.len == 255); 235 | msgClean(hdr); 236 | test_cond("msg clean", ret == 0 && hdr->mbuf_len == 1); 237 | msgFree(hdr); 238 | } 239 | test_report(); 240 | return 0; 241 | } 242 | 243 | #endif 244 | -------------------------------------------------------------------------------- /src/worker/mbuf.h: -------------------------------------------------------------------------------- 1 | // mbuf structure implemetation 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_WORKER_MBUF_H 8 | #define WHEATSERVER_WORKER_MBUF_H 9 | 10 | struct msghdr; 11 | 12 | // mbuf is a structure used to support no copy demand and learn from BSD kernel. 13 | // Wheatserver uses mbuf as send and receive buffer to store. 14 | // 15 | // Use cases: 16 | // struct msghdr *req_buf = msgCreate(); 17 | // sturct slice slice; 18 | // msgPut(req_buf, &slice); 19 | // n = read(fd, slice.data, slice.len); 20 | // msgSetWritted(req_buf, n); 21 | // ... 22 | // msgRead(req_buf, &slice); 23 | // int nparsed = parser(&slice); 24 | // msgSetReaded(req_buf, nparsed); 25 | 26 | struct msghdr *msgCreate(); 27 | void msgClean(struct msghdr *hdr); 28 | // You must call msgSetReaded after msgRead 29 | void msgRead(struct msghdr *hdr, struct slice *s); 30 | void msgSetReaded(struct msghdr *hdr, size_t len); 31 | // You must call msgSetWritted after msgPut 32 | int msgPut(struct msghdr *hdr, struct slice *s); 33 | void msgSetWritted(struct msghdr *hdr, size_t len); 34 | void msgFree(struct msghdr *hdr); 35 | // Get total size of all mbuf in `hdr` 36 | size_t msgGetSize(struct msghdr *hdr); 37 | // Check if can get unread content from `hdr` 38 | int msgCanRead(struct msghdr *hdr); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/worker/worker_async.c: -------------------------------------------------------------------------------- 1 | // Asynchronous worker module implemetation 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include "../wheatserver.h" 8 | 9 | int asyncSendData(struct conn *c); // pass `data` ownership to 10 | int asyncRecvData(struct client *c); 11 | 12 | static struct worker AsyncWorker = { 13 | NULL, NULL, asyncSendData, 14 | asyncRecvData 15 | }; 16 | 17 | struct moduleAttr AsyncWorkerAttr = { 18 | "AsyncWorker", WORKER, {.worker=&AsyncWorker}, NULL, 0, NULL, 0 19 | }; 20 | 21 | static void sendReplyToClient(struct evcenter *center, int fd, void *data, int mask) 22 | { 23 | struct client *c = data; 24 | if (!isClientValid(c)) 25 | return ; 26 | 27 | refreshClient(c); 28 | 29 | clientSendPacketList(c); 30 | if (!isClientValid(c) || !isClientNeedSend(c)) { 31 | wheatLog(WHEAT_DEBUG, "delete write event on sendReplyToClient"); 32 | deleteEvent(WorkerProcess->center, c->clifd, EVENT_WRITABLE); 33 | tryFreeClient(c); 34 | } 35 | } 36 | 37 | int asyncSendData(struct conn *c) 38 | { 39 | if (!isClientValid(c->client)) 40 | return WHEAT_WRONG; 41 | 42 | struct client *client = c->client; 43 | clientSendPacketList(client); 44 | refreshClient(client); 45 | if (!isClientValid(client)) { 46 | // This function is IO interface, we shouldn't clean client in order 47 | // to caller to deal with error. 48 | return WHEAT_WRONG; 49 | } 50 | if (isClientNeedSend(client)) { 51 | wheatLog(WHEAT_DEBUG, "create write event on asyncSendData"); 52 | createEvent(WorkerProcess->center, client->clifd, EVENT_WRITABLE, 53 | sendReplyToClient, client); 54 | } 55 | 56 | return WHEAT_OK; 57 | } 58 | 59 | int asyncRecvData(struct client *c) 60 | { 61 | if (!isClientValid(c)) 62 | return -1; 63 | ssize_t n; 64 | size_t total = 0; 65 | struct slice slice; 66 | // Because os IO notify only once if you don't read all data within this 67 | // buffer 68 | do { 69 | n = msgPut(c->req_buf, &slice); 70 | if (n != 0) { 71 | break; 72 | } 73 | n = readBulkFrom(c->clifd, &slice); 74 | if (n < 0) { 75 | setClientUnvalid(c); 76 | break; 77 | } 78 | total += n; 79 | msgSetWritted(c->req_buf, n); 80 | } while (n == slice.len); 81 | if (msgGetSize(c->req_buf) > Server.max_buffer_size) { 82 | wheatLog(WHEAT_VERBOSE, "Client buffer size larger than limit %d>%d", 83 | msgGetSize(c->req_buf), Server.max_buffer_size); 84 | setClientUnvalid(c); 85 | } 86 | refreshClient(c); 87 | return (int)total; 88 | } 89 | -------------------------------------------------------------------------------- /src/worker/worker_sync.c: -------------------------------------------------------------------------------- 1 | // Synchronous worker module implemetation 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #include "../wheatserver.h" 8 | 9 | int syncSendData(struct conn *c); // pass `data` ownership to 10 | int syncRecvData(struct client *c); 11 | 12 | static struct worker SyncWorker = { 13 | NULL, NULL, syncSendData, syncRecvData 14 | }; 15 | 16 | struct moduleAttr SyncWorkerAttr = { 17 | "SyncWorker", WORKER, {.worker=&SyncWorker}, NULL, 0, NULL, 0 18 | }; 19 | 20 | int syncSendData(struct conn *c) 21 | { 22 | if (!isClientValid(c->client)) { 23 | return -1; 24 | } 25 | struct client *client = c->client; 26 | while (isClientNeedSend(client)) { 27 | clientSendPacketList(client); 28 | refreshClient(client); 29 | if (!isClientValid(client)) { 30 | // This function is IO interface, we shouldn't clean client in order 31 | // to caller to deal with error. 32 | return WHEAT_WRONG; 33 | } 34 | } 35 | 36 | return WHEAT_OK; 37 | } 38 | 39 | int syncRecvData(struct client *c) 40 | { 41 | if (!isClientValid(c)) { 42 | return -1; 43 | } 44 | struct slice slice; 45 | ssize_t n, total = 0; 46 | do { 47 | n = msgPut(c->req_buf, &slice); 48 | if (n != 0) { 49 | break; 50 | } 51 | n = readBulkFrom(c->clifd, &slice); 52 | if (n < 0) { 53 | setClientUnvalid(c); 54 | break; 55 | } 56 | total += n; 57 | msgSetWritted(c->req_buf, n); 58 | } while (n == slice.len || n == 0); 59 | if (msgGetSize(c->req_buf) > Server.max_buffer_size) { 60 | wheatLog(WHEAT_VERBOSE, "Client buffer size larger than limit %d>%d", 61 | msgGetSize(c->req_buf), Server.max_buffer_size); 62 | setClientUnvalid(c); 63 | } 64 | refreshClient(c); 65 | 66 | return (int)total; 67 | } 68 | -------------------------------------------------------------------------------- /src/wstr.h: -------------------------------------------------------------------------------- 1 | // wstr, A C dynamic strings library, referenced from Redis 2 | // 3 | // Copyright (c) 2013 The Wheatserver Author. All rights reserved. 4 | // Use of this source code is governed by a BSD-style license that can be 5 | // found in the LICENSE file. See the AUTHORS file for names of contributors. 6 | 7 | #ifndef WHEATSERVER_WSTR_H 8 | #define WHEATSERVER_WSTR_H 9 | 10 | #include 11 | 12 | #define MAX_STR 16*1024*1024 13 | 14 | // 1. Return NULL means call func failed but the old value passed in stay 15 | // alive. 16 | // 17 | // 2. `len` in struct wstrhd stand for the length of string. 18 | 19 | typedef char *wstr; 20 | 21 | // wstr memory format: 22 | // | len | free | xxxxxxxxxxxxxx | 23 | // | | | 24 | // buf | | 25 | // | | | 26 | // <------len-----> | 27 | // | <---free---> 28 | // wstr put `len` and `free` fields into header bytes and return pointer points 29 | // to buf. 30 | // Advantage: 31 | // * compatible with methods in 32 | // * avoid destroy header because of buffer overflow 33 | // 34 | // `len`: reprensented for the length of `buf` 35 | // `free`: reprensented for the free length of `buf` 36 | 37 | struct wstrhd { 38 | int len; 39 | int free; 40 | char buf[]; 41 | }; 42 | 43 | static inline int wstrlen(const wstr s) 44 | { 45 | struct wstrhd *hd = (struct wstrhd *)(s - sizeof(struct wstrhd)); 46 | return hd->len; 47 | } 48 | 49 | static inline int wstrfree(const wstr s) 50 | { 51 | struct wstrhd *hd = (struct wstrhd *)(s - sizeof(struct wstrhd)); 52 | return hd->free; 53 | } 54 | 55 | // wstrupdatelen is used to handle scene below: 56 | // int ret; 57 | // wstr str = wstrNewLen(NULL, 100); 58 | // ret = read(fd, str, 100); 59 | // wstrupdatelen(str, ret); 60 | // As you can see, when expand or trunct buffer, we should modify wstrhd header 61 | // to suite buffer by hand. 62 | static inline void wstrupdatelen(wstr s, int len) 63 | { 64 | struct wstrhd *hd = (struct wstrhd *)(s - sizeof(struct wstrhd)); 65 | hd->free += (hd->len - len); 66 | hd->len = len; 67 | hd->buf[len] = '\0'; 68 | } 69 | 70 | // If `init` is not NULL, create wstr and copy `init_len` length from `init` 71 | // into wstr. wstrlen(wstr) == initlen && wstrfree(wstr) == 0 72 | // If `init` is NULL and init_len is not zero, create `init_len` length buffer 73 | // and set set free `init_len` 74 | wstr wstrNewLen(const void *init, int init_len); 75 | wstr wstrNew(const void *init); 76 | wstr wstrEmpty(); 77 | void wstrFree(wstr s); 78 | // Use case: 79 | // wstr s = wstrNew("1 2 3 4 5 6"); 80 | // int count; 81 | // wstr *segements = wstrNewSplit(s, " ", 1, &count); 82 | // assert(count == 6); 83 | // assert(strcmp(segements[0], "1") == 0) 84 | // assert(strcmp(segements[5], "6") == 0) 85 | // .... 86 | // wstrFreeSplit(segements, count); 87 | // More example and special cases you can see unit tests in wstr.c 88 | wstr *wstrNewSplit(wstr s, const char *sep, int sep_len, int *count); 89 | void wstrFreeSplit(wstr *slots, int count); 90 | wstr wstrDup(const wstr s); 91 | int wstrCmp(const wstr s1, const wstr s2); 92 | int wstrCmpChars(const wstr s1, const char *s2, size_t len); 93 | int wstrCmpNocaseChars(const wstr s1, const char *s2, size_t l2); 94 | void wstrLower(wstr s); 95 | void wstrUpper(wstr s); 96 | int wstrIndex(wstr s, const char t); 97 | int wstrStartWithChars(const wstr s, const char *s2, size_t len); 98 | int wstrStartWith(const wstr s, const wstr s2); 99 | 100 | wstr wstrCat(wstr s, const char *t); 101 | wstr wstrCatLen(wstr s, const char *t, size_t len); 102 | wstr wstrRange(wstr, int left, int right); 103 | wstr wstrStrip(wstr, const char *chars); 104 | wstr wstrRemoveFreeSpace(wstr); 105 | void wstrClear(wstr); 106 | 107 | // Low function 108 | // wstrMakeRoom is used to make more space room to store when meet large bulk 109 | // buffer. 110 | // Case: 111 | // int ret; 112 | // wstr str = wstrNewLen(NULL, 100); 113 | // ret = read(fd, str, 100); 114 | // wstrupdatelen(str, ret); 115 | // .... 116 | // str = wstrMakeRoom(str, 1000); 117 | // ret = read(fd, str+wstr(len), wstrfree(str)); 118 | // wstrupdatelen(str, ret); 119 | wstr wstrMakeRoom(wstr s, size_t add_size); 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /tests/list_conf.conf: -------------------------------------------------------------------------------- 1 | redis-servers 2 | -------------------------------------------------------------------------------- /tests/list_conf1.conf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuyuyu101/wheatserver/25dfb895f1d18ad7b7727aae24f0e65be4708c27/tests/list_conf1.conf -------------------------------------------------------------------------------- /tests/redis.conf: -------------------------------------------------------------------------------- 1 | redis-servers 2 | - 127.0.0.1:18000 3 | - 127.0.0.1:18001 4 | -------------------------------------------------------------------------------- /tests/reload.conf: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | worker-number 4 4 | -------------------------------------------------------------------------------- /tests/test_async.py: -------------------------------------------------------------------------------- 1 | from wheatserver_test import WheatServer, server_socket, construct_command, PROJECT_PATH 2 | import os 3 | import time 4 | import signal 5 | 6 | async_server = None 7 | 8 | def setup_module(module): 9 | global async_server 10 | async_server = WheatServer("", "--worker-type %s" % "AsyncWorker", 11 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 12 | "--timeout-seconds 3", "--stat-refresh-time 1", 13 | "--protocol Http") 14 | time.sleep(0.5) 15 | 16 | 17 | def teardown_module(module): 18 | global async_server 19 | del async_server 20 | -------------------------------------------------------------------------------- /tests/test_conf.py: -------------------------------------------------------------------------------- 1 | from wheatserver_test import WheatServer, PROJECT_PATH, server_socket 2 | from time import sleep 3 | import os 4 | 5 | def test_conf_list(): 6 | async = WheatServer("-t", "%s" % os.path.join(os.path.join(PROJECT_PATH, "tests"), "list_conf.conf")) 7 | sleep(0.1) 8 | process_id, status = os.waitpid(async.exec_pid, 0) 9 | assert status == 0 10 | 11 | def test_conf_list1(): 12 | async = WheatServer("-t", "%s" % os.path.join(os.path.join(PROJECT_PATH, "tests"), "list_conf1.conf")) 13 | sleep(0.1) 14 | process_id, status = os.waitpid(async.exec_pid, 0) 15 | assert status == 0 16 | -------------------------------------------------------------------------------- /tests/test_http.py: -------------------------------------------------------------------------------- 1 | from wheatserver_test import WheatServer, PROJECT_PATH, server_socket 2 | import os 3 | import time 4 | import requests 5 | 6 | POST_DATA = b"""POST /asdf HTTP/1.1\r\nHost: 127.0.0.1:10828\r\nContent-Length: 200\r\nContent-Type: multipart/form-data; boundary=25510934abe14960a7309cc7a2c790d8\r\nAccept-Encoding: gzip, deflate, compress\r\nAccept: */*\r\nUser-Agent: python-requests/1.1.0 CPython/2.7.2 Darwin/12.2.0\r\n\r\n--25510934abe14960a7309cc7a2c790d8\r\nContent-Disposition: form-data; name=\"file\"; filename=\"/Users/wanghaomai/Downloads/1.txt\"\r\nContent-Type: text/plain\r\n\r\n1234\n\r\n--25510934abe14960a7309cc7a2c790d8--\r\n""" 7 | 8 | def test_get_two(): 9 | async = WheatServer("", "--worker-type %s" % "AsyncWorker", 10 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 11 | "--document-root %s" % os.path.join(PROJECT_PATH, "example/"), 12 | "--allowed-extension bmp,gif", 13 | "--protocol Http") 14 | time.sleep(0.1) 15 | s = server_socket(10828) 16 | s.send("GET / HTTP/1.1\r\nHOST: 127.0.0.1:10828\r\nConnec") 17 | time.sleep(0.2) 18 | s.send("tion: close\r\n\r\n") 19 | a = s.recv(100) 20 | assert "200" in a 21 | 22 | def test_post_file(): 23 | async = WheatServer("", "--worker-type %s" % "AsyncWorker", 24 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 25 | "--document-root %s" % os.path.join(PROJECT_PATH, "example/"), 26 | "--allowed-extension bmp,gif", 27 | "--protocol Http") 28 | time.sleep(0.1) 29 | s = server_socket(10828) 30 | s.settimeout(0.5) 31 | s.send(POST_DATA[:302]) 32 | time.sleep(0.1) 33 | try: 34 | a = s.recv(100) 35 | except: 36 | pass 37 | else: 38 | assert "" == a 39 | s.send(POST_DATA[302:]) 40 | a = s.recv(1000) 41 | assert "200" in a and "1234" in a 42 | 43 | def test_document_root_and_static_file_dir(): 44 | async = WheatServer("", "--worker-type %s" % "AsyncWorker", 45 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 46 | "--document-root %s" % PROJECT_PATH + "/example/", 47 | "--static-file-dir %s" % "/static", 48 | "--allowed-extension bmp,gif,jpg", 49 | "--protocol Http") 50 | time.sleep(0.1) 51 | r = requests.get("http://127.0.0.1:10828/static/example.jpg",timeout=1) 52 | assert 200 == r.status_code 53 | -------------------------------------------------------------------------------- /tests/test_redis.py: -------------------------------------------------------------------------------- 1 | from wheatserver_test import WheatServer, PROJECT_PATH, server_socket 2 | import time 3 | import os 4 | import redis 5 | import signal 6 | 7 | class RedisServer(object): 8 | def __init__(self, *options): 9 | self.exec_pid = os.fork() 10 | if not self.exec_pid: 11 | os.execlp("redis-server", *options) 12 | 13 | def __del__(self): 14 | os.kill(self.exec_pid, signal.SIGQUIT); 15 | 16 | 17 | def test_redis(): 18 | async = WheatServer("", "--worker-type %s" % "AsyncWorker", 19 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 20 | "--document-root %s" % PROJECT_PATH + "/example/", 21 | "--static-file-dir %s" % "/static", 22 | "--protocol Redis") 23 | time.sleep(0.1) 24 | r = redis.StrictRedis(port=10828) 25 | for i in range(100): 26 | time.sleep(0.001) 27 | assert r.set(str(i), i) 28 | for i in range(100): 29 | time.sleep(0.001) 30 | assert r.get(str(i)) == str(i) 31 | 32 | assert r.get('z') == None 33 | 34 | def test_redis_conf(): 35 | redis1 = RedisServer("", "--port 18000") 36 | redis2 = RedisServer("", "--port 18001") 37 | async = WheatServer("redis.conf", "--worker-type %s" % "AsyncWorker", 38 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 39 | "--document-root %s" % PROJECT_PATH + "/example/", 40 | "--static-file-dir %s" % "/static", 41 | "--protocol Redis", 42 | "--config-source RedisThenFile", 43 | "--config-server 127.0.0.1:18000", 44 | "--logfile test_redis_conf.log" 45 | ) 46 | time.sleep(0.1) 47 | r = redis.StrictRedis(port=10828) 48 | assert r.set(1, 1) 49 | assert r.get(1) == '1' 50 | f = open("test_redis_conf.log") 51 | content = f.read(10000) 52 | f.close() 53 | assert "get config from redis failed" in content 54 | assert "get config from file successful" in content 55 | assert "Save config to redis success" in content 56 | del async 57 | os.unlink("test_redis_conf.log") 58 | async = WheatServer("redis.conf", "--worker-type %s" % "AsyncWorker", 59 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 60 | "--document-root %s" % PROJECT_PATH + "/example/", 61 | "--static-file-dir %s" % "/static", 62 | "--protocol Redis", 63 | "--config-source RedisThenFile", 64 | "--config-server 127.0.0.1:18000", 65 | "--logfile test_redis_conf1.log", 66 | "--port 10826", "--stat-port 10827" 67 | ) 68 | time.sleep(0.1) 69 | r = redis.StrictRedis(port=10826) 70 | assert r.get(1) == '1' 71 | f = open("test_redis_conf1.log") 72 | content = f.read(10000) 73 | f.close() 74 | assert "get config from redis server sucessful" in content 75 | assert "Save config to redis success" not in content 76 | os.unlink("test_redis_conf1.log") 77 | del async 78 | async = WheatServer("redis.conf", "--worker-type %s" % "AsyncWorker", 79 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 80 | "--document-root %s" % PROJECT_PATH + "/example/", 81 | "--static-file-dir %s" % "/static", 82 | "--protocol Redis", 83 | "--config-source UseFile", 84 | "--config-server 127.0.0.1:18000", 85 | "--logfile test_redis_conf2.log", 86 | "--port 10824", "--stat-port 10825" 87 | ) 88 | time.sleep(0.1) 89 | r = redis.StrictRedis(port=10824) 90 | assert r.get(1) == '1' 91 | f = open("test_redis_conf2.log") 92 | content = f.read(10000) 93 | f.close() 94 | assert "get config from file successful" in content 95 | assert "Save config to redis success" not in content 96 | assert "get config from redis server sucessful" not in content 97 | os.unlink("test_redis_conf2.log") 98 | del async 99 | -------------------------------------------------------------------------------- /tests/test_reload.py: -------------------------------------------------------------------------------- 1 | from wheatserver_test import WheatServer, server_socket, construct_command, PROJECT_PATH 2 | import os 3 | import signal 4 | import time 5 | 6 | async_server = None 7 | 8 | def test_reload(): 9 | async_server = WheatServer("reload.conf", "--worker-type %s" % "AsyncWorker", 10 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 11 | "--protocol Http") 12 | time.sleep(1) 13 | os.kill(async_server.exec_pid, signal.SIGHUP) 14 | s = server_socket(10829) 15 | 16 | s.send(construct_command("stat", "master")) 17 | assert "Total spawn workers: 8" in s.recv(1000) 18 | -------------------------------------------------------------------------------- /tests/test_static_allowed_extension.py: -------------------------------------------------------------------------------- 1 | from wheatserver_test import WheatServer, PROJECT_PATH 2 | import os 3 | import time 4 | import httplib 5 | 6 | async_server = None 7 | 8 | def test_allowed_extension(): 9 | async_server = WheatServer("", "--worker-type %s" % "AsyncWorker", 10 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 11 | "--document-root %s" % os.path.join(PROJECT_PATH, "example/"), 12 | "--allowed-extension bmp,gif", 13 | "--protocol Http") 14 | time.sleep(0.5) 15 | conn = httplib.HTTPConnection("127.0.0.1", 10828, timeout=10); 16 | conn.request("GET", "/static/example.jpg") 17 | r1 = conn.getresponse() 18 | assert r1.status == 404 19 | 20 | def test_allowed_extension2(): 21 | async_server = WheatServer("", "--worker-type %s" % "AsyncWorker", 22 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 23 | "--document-root %s" % os.path.join(PROJECT_PATH, "example/"), 24 | "--allowed-extension jpg", 25 | "--protocol Http") 26 | time.sleep(0.5) 27 | conn = httplib.HTTPConnection("127.0.0.1", 10828, timeout=10); 28 | conn.request("GET", "/static/example.jpg") 29 | r1 = conn.getresponse() 30 | assert r1.status == 200 31 | conn = httplib.HTTPConnection("127.0.0.1", 10828, timeout=10); 32 | conn.request("GET", "/static/") 33 | r1 = conn.getresponse() 34 | assert r1.status == 404 35 | 36 | def test_allowed_extension3(): 37 | async_server = WheatServer("", "--worker-type %s" % "AsyncWorker", 38 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 39 | "--document-root %s" % os.path.join(PROJECT_PATH, "example/"), 40 | "--allowed-extension *", 41 | "--protocol Http") 42 | time.sleep(0.5) 43 | conn = httplib.HTTPConnection("127.0.0.1", 10828, timeout=10); 44 | conn.request("GET", "/static/example.jpg") 45 | r1 = conn.getresponse() 46 | assert r1.status == 200 47 | del async_server 48 | -------------------------------------------------------------------------------- /tests/wheatserver_test.py: -------------------------------------------------------------------------------- 1 | import httplib 2 | import os 3 | import signal 4 | import socket 5 | import time 6 | 7 | PROJECT_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 8 | EXECFILE = os.path.join(os.path.join(PROJECT_PATH, "src"), "wheatserver") 9 | 10 | class WheatServer(object): 11 | def __init__(self, conf_file="", *options): 12 | assert os.access(EXECFILE, os.F_OK) 13 | self.exec_pid = os.fork() 14 | if not conf_file: 15 | conf_file = os.path.join(PROJECT_PATH, "wheatserver.conf") 16 | if not self.exec_pid: 17 | os.execl(EXECFILE, EXECFILE, conf_file, *options) 18 | 19 | def __del__(self): 20 | os.kill(self.exec_pid, signal.SIGQUIT); 21 | 22 | def server_socket(port): 23 | s = socket.socket() 24 | s.connect(("127.0.0.1", port)) 25 | return s 26 | 27 | def construct_command(*args): 28 | return "\r\r%s$" % ("\n".join(args)) 29 | 30 | def pytest_generate_tests(metafunc): 31 | metafunc.parametrize(('port',), [(10827,),(10829,),]) 32 | 33 | def test_config_command(port): 34 | s = server_socket(port) 35 | s.send(construct_command("config", "logfile-level")) 36 | assert s.recv(100) == "logfile-level: DEBUG" 37 | 38 | def test_stat_accuracy(port): 39 | global sync_server, async_server 40 | for i in range(100): 41 | conn = httplib.HTTPConnection("127.0.0.1", port-1, timeout=1); 42 | conn.request("GET", "/") 43 | r1 = conn.getresponse() 44 | assert r1.status == 200 45 | time.sleep(0.1) 46 | os.kill(sync_server.exec_pid, signal.SIGUSR1) 47 | os.kill(async_server.exec_pid, signal.SIGUSR1) 48 | time.sleep(0.1) 49 | s = server_socket(port) 50 | s.send(construct_command("stat", "master")) 51 | assert "Total client: 100" in s.recv(1000) 52 | 53 | def test_static_file(port): 54 | time.sleep(0.1) 55 | for i in range(10): 56 | conn = httplib.HTTPConnection("127.0.0.1", port-1, timeout=1); 57 | conn.request("GET", "/static/example.jpg") 58 | r1 = conn.getresponse() 59 | assert r1.status == 200 60 | 61 | sync_server = async_server = None 62 | 63 | def setup_module(module): 64 | global sync_server, async_server 65 | sync_server = WheatServer("", "--port 10826", "--stat-port 10827", 66 | "--worker-type %s" % "SyncWorker", 67 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 68 | "--document-root %s" % os.path.join(PROJECT_PATH, "example/"), 69 | "--static-file-dir /static/", 70 | "--protocol Http") 71 | 72 | async_server = WheatServer("", "--worker-type %s" % "AsyncWorker", 73 | "--app-project-path %s" % os.path.join(PROJECT_PATH, "example"), 74 | "--document-root %s" % os.path.join(PROJECT_PATH, "example/"), 75 | "--static-file-dir /static/", 76 | "--protocol Http") 77 | time.sleep(0.5) 78 | 79 | def teardown_module(module): 80 | global sync_server, async_server 81 | del sync_server, async_server 82 | --------------------------------------------------------------------------------