├── .gitignore
├── .travis.yml
├── LICENSE
├── Makefile
├── README.md
├── benchmark
└── http_benchmark.lua
├── deps
└── .gitignore
├── doc
├── fs.md
├── http.md
├── loop.md
├── parallel.md
├── process.md
├── system.md
├── tcp.md
├── timer.md
└── url.md
├── src
├── uv.lua
└── uv
│ ├── ctypes
│ ├── init.lua
│ ├── uv_buf_t.lua
│ ├── uv_check_t.lua
│ ├── uv_connect_t.lua
│ ├── uv_fs_t.lua
│ ├── uv_getaddrinfo_t.lua
│ ├── uv_handle_t.lua
│ ├── uv_idle_t.lua
│ ├── uv_loop_t.lua
│ ├── uv_prepare_t.lua
│ ├── uv_process_options_t.lua
│ ├── uv_process_t.lua
│ ├── uv_signal_t.lua
│ ├── uv_stream_t.lua
│ ├── uv_tcp_t.lua
│ ├── uv_timer_t.lua
│ └── uv_write_t.lua
│ ├── fs.lua
│ ├── http.lua
│ ├── init.lua
│ ├── lib
│ └── .gitignore
│ ├── libc.lua
│ ├── libhttp_parser.lua
│ ├── libuv.lua
│ ├── libuv2.c
│ ├── libuv2.h
│ ├── libuv2.lua
│ ├── loop.lua
│ ├── parallel.lua
│ ├── process.lua
│ ├── system.lua
│ ├── tcp.lua
│ ├── timer.lua
│ ├── url.lua
│ └── util
│ ├── async.lua
│ ├── class.lua
│ ├── ctype.lua
│ ├── errno.lua
│ ├── expect.lua
│ ├── join.lua
│ ├── strict.lua
│ └── verify.lua
└── test
├── fs_test.lua
├── http_test.lua
├── loop_test.lua
├── parallel_test.lua
├── process_test.lua
├── system_test.lua
├── tcp_test.lua
├── timer_test.lua
└── url_test.lua
/.gitignore:
--------------------------------------------------------------------------------
1 | /deps
2 | /luajit
3 | /src/uv/lib
4 | *.swp
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: erlang
2 |
3 | install:
4 | - make
5 |
6 | script:
7 | - make test
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Preston Guillory
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | LUA = ./luajit
2 | LUA_DIR=/usr/local
3 | LUA_LIBDIR=$(LUA_DIR)/lib/lua/5.1
4 | LUA_SHAREDIR=$(LUA_DIR)/share/lua/5.1
5 |
6 | EXT ?= so
7 | ifeq ($(shell uname -s), Darwin)
8 | EXT = dylib
9 | endif
10 |
11 | FILES=src/uv/lib/libuv.$(EXT) \
12 | src/uv/lib/libuv.min.h \
13 | src/uv/lib/libuv2.$(EXT) \
14 | src/uv/lib/libuv2.min.h \
15 | src/uv/lib/libhttp_parser.$(EXT) \
16 | src/uv/lib/libhttp_parser.min.h
17 |
18 | all: $(FILES)
19 |
20 | ################################################################################
21 | # libuv
22 | ################################################################################
23 |
24 | deps/libuv-v0.11.28.zip:
25 | wget https://github.com/joyent/libuv/archive/v0.11.28.zip -O $@
26 |
27 | deps/libuv-0.11.28: deps/libuv-v0.11.28.zip
28 | rm -rf $@
29 | unzip $< -d deps
30 | touch $@
31 |
32 | deps/libuv: deps/libuv-0.11.28
33 | cd deps && ln -fs libuv-0.11.28 libuv
34 |
35 | deps/libuv/include/uv.h: deps/libuv
36 | deps/libuv/autogen.sh: deps/libuv
37 |
38 | deps/libuv/configure: deps/libuv/autogen.sh
39 | cd deps/libuv && sh autogen.sh
40 |
41 | deps/libuv/Makefile: deps/libuv/configure
42 | cd deps/libuv && ./configure
43 |
44 | deps/libuv/.libs/libuv.a: deps/libuv/Makefile
45 | cd deps/libuv && make
46 |
47 | deps/libuv/.libs/libuv.$(EXT): deps/libuv/.libs/libuv.a
48 |
49 | src/uv/lib/libuv.$(EXT): deps/libuv/.libs/libuv.$(EXT)
50 | cp $+ $@
51 |
52 | src/uv/lib/libuv.min.h: deps/libuv/include/uv.h
53 | gcc -E $+ | grep -v '^ *#' > $@
54 |
55 | src/uv/lib/libuv2.dylib: deps/libuv/.libs/libuv.a src/uv/libuv2.c
56 | gcc -dynamiclib $+ -o $@
57 |
58 | src/uv/lib/libuv2.so: src/uv/libuv2.c deps/libuv/.libs/libuv.so
59 | gcc -g -fPIC -shared $+ -o $@
60 |
61 | src/uv/lib/libuv2.min.h: src/uv/libuv2.h
62 | gcc -E $+ | grep -v '^ *#' > $@
63 |
64 |
65 | ################################################################################
66 | # http-parser
67 | ################################################################################
68 |
69 | deps/http-parser-v2.3.zip:
70 | wget https://github.com/joyent/http-parser/archive/v2.3.zip -O $@
71 |
72 | deps/http-parser-2.3: deps/http-parser-v2.3.zip
73 | rm -rf $@
74 | unzip $< -d deps
75 | touch $@
76 |
77 | deps/http-parser: deps/http-parser-2.3
78 | cd deps && ln -fs http-parser-2.3 http-parser
79 |
80 | deps/http-parser/http_parser.h: deps/http-parser
81 |
82 | deps/http-parser/libhttp_parser.so.2.3: deps/http-parser
83 | cd deps/http-parser && make library
84 |
85 | src/uv/lib/libhttp_parser.$(EXT): deps/http-parser/libhttp_parser.so.2.3
86 | cp $+ $@
87 |
88 | src/uv/lib/libhttp_parser.min.h: deps/http-parser/http_parser.h
89 | gcc -E $+ | grep -v '^ *#' > $@
90 |
91 | ################################################################################
92 | # luajit
93 | ################################################################################
94 |
95 | deps/LuaJIT-2.0.3.tar.gz:
96 | cd deps && wget http://luajit.org/download/LuaJIT-2.0.3.tar.gz
97 |
98 | deps/LuaJIT-2.0.3: deps/LuaJIT-2.0.3.tar.gz
99 | rm -rf $@
100 | cd deps && tar zxf LuaJIT-2.0.3.tar.gz
101 | touch $@
102 |
103 | deps/LuaJIT-2.0.3/Makefile: deps/LuaJIT-2.0.3
104 |
105 | deps/LuaJIT-2.0.3/src/luajit: deps/LuaJIT-2.0.3/Makefile
106 | cd deps/LuaJIT-2.0.3 && make
107 |
108 | luajit: deps/LuaJIT-2.0.3/src/luajit
109 | cp $+ $@
110 |
111 | ################################################################################
112 | # etc...
113 | ################################################################################
114 |
115 | install: all uninstall
116 | cp -R src/uv ${LUA_SHAREDIR}/
117 |
118 | uninstall:
119 | rm -rf ${LUA_SHAREDIR}/uv
120 |
121 | clean:
122 | rm -rf deps/* src/uv/lib/*
123 |
124 | test: run-tests
125 | run-tests: $(LUA)
126 | LUA_PATH="src/?.lua;;" find test -name "*_test.lua" -exec luajit "{}" ";"
127 | @echo All tests passing
128 |
129 | benchmark: run-benchmarks
130 | run-benchmarks: $(LUA)
131 | LUA_PATH="src/?.lua;;" find benchmark -name "*_benchmark.lua" -exec luajit "{}" ";"
132 | @echo All tests passing
133 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | luajit-libuv [](https://travis-ci.org/pguillory/luajit-libuv)
2 | ============
3 |
4 | This project provides a [LuaJIT FFI] binding to [libuv], the async I/O library
5 | powering [Node.js]. It uses Lua coroutines to provide non-blocking I/O with
6 | synchronous syntax.
7 |
8 | For example, you can build a web server that performs I/O (like reading a file
9 | or talking to a database) while generating each response, and it will process
10 | multiple requests simultaneously.
11 |
12 | ```lua
13 | local http = require 'uv.http'
14 | local fs = require 'uv.fs'
15 |
16 | http.listen('127.0.0.1', 8080, function(request)
17 | return { status = 200, body = fs.readfile('README.md') }
18 | end)
19 | ```
20 |
21 | Or you can perform multiple HTTP requests simultaneously.
22 |
23 | ```lua
24 | local http = require 'uv.http'
25 | local parallel = require 'uv.parallel'
26 |
27 | local requests = {
28 | { url = 'http://www.google.com/' },
29 | { url = 'http://www.bing.com/' },
30 | { url = 'http://www.amazon.com/' },
31 | }
32 |
33 | local responses = parallel.map(requests, http.request)
34 | ```
35 |
36 | Status
37 | ------
38 |
39 | Not production ready. Under active development. The API is unstable.
40 |
41 | Requirements
42 | ------------
43 |
44 | - [LuaJIT]. Regular Lua won't run it. That said, you probably want LuaJIT
45 | anyway.
46 |
47 | - Standard build tools.
48 |
49 | - [libuv] and [http-parser] are bundled and do not need to be installed
50 | separately.
51 |
52 | Installation
53 | ------------
54 |
55 | ```bash
56 | git clone https://github.com/pguillory/luajit-libuv.git
57 | cd luajit-libuv
58 | make
59 | make install
60 | ```
61 |
62 | API Reference
63 | -------------
64 |
65 | Functions are divided into submodules. Each submodule can either be required directly or accessed indirectly through the `uv` module:
66 |
67 | ```lua
68 | local fs = require 'uv.fs'
69 |
70 | local uv = require 'uv'
71 | local fs = uv.fs
72 | ```
73 |
74 | * [uv.fs](doc/fs.md) - File system
75 | * [uv.http](doc/http.md) - HTTP client and server
76 | * [uv.loop](doc/loop.md) - Event loop control
77 | * [uv.parallel](doc/parallel.md) - Parallel processing
78 | * [uv.process](doc/process.md) - Process management
79 | * [uv.system](doc/system.md) - System utility functions
80 | * [uv.timer](doc/timer.md) - Timers
81 | * [uv.url](doc/url.md) - URL parsing and encoding
82 |
83 | Contributing
84 | ------------
85 |
86 | Your contributions are welcome! Please verify that `make test` succeeds and
87 | submit your changes as a pull request.
88 |
89 | See Also
90 | --------
91 |
92 | Other people have done things like this.
93 |
94 | - [luvit](https://github.com/luvit/luvit)
95 | - [LuaNode](https://github.com/ignacio/LuaNode)
96 | - [lev](https://github.com/connectFree/lev)
97 | - [luv](https://github.com/luvit/luv)
98 | - [luauv](https://github.com/grrrwaaa/luauv)
99 | - [uv](https://github.com/steveyen/uv)
100 | - [lua-uv](https://github.com/bnoordhuis/lua-uv/)
101 | - [Ray](https://github.com/richardhundt/luv/tree/ray)
102 |
103 | [Luajit FFI]: http://luajit.org/ext_ffi.html
104 | [libuv]: https://github.com/joyent/libuv
105 | [Node.js]: http://nodejs.org/
106 | [luv]: https://github.com/creationix/luv
107 | [http-parser]: https://github.com/joyent/http-parser
108 | [LuaJIT]: http://luajit.org/
109 | [FIFO]: http://en.wikipedia.org/wiki/Named_pipe
110 |
--------------------------------------------------------------------------------
/benchmark/http_benchmark.lua:
--------------------------------------------------------------------------------
1 | local loop = require 'uv.loop'
2 | local http = require 'uv.http'
3 | local system = require 'uv.system'
4 |
5 | loop.run(function()
6 | local server = http.listen('127.0.0.1', 7000, function(request)
7 | return 200, {}, 'ok'
8 | end)
9 | local count = 1000
10 | local time1 = system.hrtime()
11 | for i = 1, count do
12 | http.request { url = 'http://127.0.0.1:7000/' }
13 | end
14 | local time2 = system.hrtime()
15 | print(count .. ' requests took ' .. (time2 - time1) .. ' seconds')
16 | server:close()
17 | end)
18 |
--------------------------------------------------------------------------------
/deps/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pguillory/luajit-libuv/dc0ec6e9de02011783d01ff99788be27b444c53e/deps/.gitignore
--------------------------------------------------------------------------------
/doc/fs.md:
--------------------------------------------------------------------------------
1 | API Reference - uv.fs
2 | =====================
3 |
4 | The `fs` module provides access to the file system.
5 |
6 | ### fs.open(path, flags, mode)
7 |
8 | Open a file. `flags` can have the following values:
9 |
10 | - `r`: reading
11 | - `w`: writing, truncate it if it exists, otherwise create it
12 | - `a`: appending if it exists, otherwise create it
13 | - `r+`: reading and writing
14 | - `w+`: reading and writing, truncate it if it exists, otherwise create it
15 | - `a+`: reading and appending if it exists, otherwise create it
16 |
17 | `mode` sets access permissions on the file if it is created. It can be a
18 | number or an octal string. '700' means a file is only accessible by you. '777'
19 | means it is accessible by anyone.
20 |
21 | Returns a file object.
22 |
23 | ```lua
24 | local file = fs.open('/path/to/file.txt', 'w', '755')
25 | file:write('hello world')
26 | file:close()
27 | ```
28 |
29 | ### fs.unlink(path)
30 |
31 | Delete a file.
32 |
33 | ### fs.mkdir(path, mode)
34 |
35 | Create a directory. See `fs.open` for documentation on `mode`.
36 |
37 | ### fs.rmdir(path)
38 |
39 | Delete a directory. It must be empty.
40 |
41 | ### fs.chmod(path, mode)
42 |
43 | Change the permissions of a file/directory. See `fs.open` for documentation on
44 | `mode`.
45 |
46 | ### fs.chown(path, uid, gid)
47 |
48 | Change the owner of a file/directory. `uid` and `gid` should be numbers.
49 |
50 | ### fs.stat(path)
51 |
52 | Retrieve information about a file/directory. Returns a table with the following keys:
53 |
54 | - `uid`: User who owns the file.
55 | - `gid`: Group that owns the file.
56 | - `size`: File size in bytes.
57 | - `mode`: Access permissions. See `fs.open`.
58 | - `is_dir`: `true` if it is a directory.
59 | - `is_fifo`: `true` if it is a [FIFO].
60 | - `atime`: Time the file was last accessed (second precision).
61 | - `atimensec`: Time the file was last accessed (nanosecond precision).
62 | - `mtime`: Time the file was last accessed (second precision).
63 | - `mtimensec`: Time the file was last accessed (nanosecond precision).
64 | - `ctime`: Time the file was last changed (second precision).
65 | - `ctimensec`: Time the file was last changed (nanosecond precision).
66 | - `birthtime`: Time the file was created (second precision).
67 | - `birthtimensec`: Time the file was created (nanosecond precision).
68 |
69 | ### fs.lstat(path)
70 |
71 | Same as `fs.stat`, but doesn't follow symlinks.
72 |
73 | ### fs.rename(path, new_path)
74 |
75 | Rename a file.
76 |
77 | ### fs.link(path, new_path)
78 |
79 | Create a hard link.
80 |
81 | ### fs.symlink(path, new_path)
82 |
83 | Create a symlink.
84 |
85 | ### fs.readlink(path)
86 |
87 | Read the value of a symlink.
88 |
89 | ### fs.readfile(path)
90 |
91 | Get the full contents of a file as a string.
92 |
93 | ### fs.writefile(path, body)
94 |
95 | Write a string to a file. Truncates the file if it already exists.
96 |
97 | ### fs.tmpname()
98 |
99 | Get a temporary filename.
100 |
101 | ### fs.cwd()
102 |
103 | Get the current working directory.
104 |
105 | ### fs.chdir(dir)
106 |
107 | Change the current working directory.
108 |
109 | ### fs.readdir(path)
110 |
111 | Get a list of files/subdirectories in a directory.
112 |
113 | ### fs.readdir_r(path)
114 |
115 | Get a list of all files under a directory and its descendent subdirectories.
116 | Returns a flat list of filenames.
117 |
118 | ```lua
119 | fs.with_tempdir(function(dir)
120 | fs.chdir(dir)
121 | fs.writefile('a', '')
122 | fs.writefile('b', '')
123 | fs.mkdir('c')
124 | fs.writefile('c/d', '')
125 | local filenames = fs.readdir_r('.')
126 | # filenames == { 'a', 'b', 'c/d' }
127 | end)
128 | ```
129 |
130 | ### fs.rm_rf(path)
131 |
132 | Delete a file or directory. If it is a directory, its contents are deleted as well. *Be careful with this one!*
133 |
134 | ### fs.dirname(filename)
135 |
136 | ### fs.basename(filename)
137 |
138 | ### fs.extname(filename)
139 |
140 | Extract the directory, basename, and extension from a filename, respectively.
141 |
142 | ```lua
143 | assert(fs.dirname ('/path/to/file.txt') == '/path/to/')
144 | assert(fs.basename('/path/to/file.txt') == 'file')
145 | assert(fs.extname ('/path/to/file.txt') == '.txt')
146 | ```
147 |
148 | ### fs.with_tempdir(callback)
149 |
150 | Create a directory, pass it to a callback, and delete the directory when the
151 | callback returns.
152 |
153 | ```lua
154 | fs.with_tempdir(function(dir)
155 | # dir will be something like '/tmp/lua_bVjBeR'
156 | end)
157 | ```
158 |
159 |
--------------------------------------------------------------------------------
/doc/http.md:
--------------------------------------------------------------------------------
1 | API Reference - uv.http
2 | =======================
3 |
4 | The `http` module provides both a client and a server.
5 |
6 | ### http.request(request)
7 |
8 | Send an HTTP request and return the response. `request` should be a table with
9 | the following fields:
10 |
11 | - `method`: An HTTP method. Defaults to 'GET'.
12 |
13 | - `url`: The full URL to request. Any parts that are present will override the
14 | corresponding options below.
15 |
16 | - `scheme`: Defaults to 'http'.
17 |
18 | - `host`: Either an IP or DNS name is acceptable.
19 |
20 | - `port`: Defaults to 80.
21 |
22 | - `path`: Defaults to '/'.
23 |
24 | - `query`: Query string. Optional.
25 |
26 | - `body`: Request body, for POST requests.
27 |
28 | - `ip`: The IP address of the connected client.
29 |
30 | The value returned will be a table containing the following fields:
31 |
32 | - `status`: The HTTP status. 200 on success, or 400+ on error.
33 |
34 | - `headers`: A table of HTTP response headers.
35 |
36 | - `body`: The response body, as a string.
37 |
38 | ```lua
39 | local response = http.request { url = 'http://example.com/page1' }
40 |
41 | -- or
42 |
43 | local response = http.request { host = 'example.com', path = '/page1' }
44 | ```
45 |
46 | ### http.listen(host, port, callback)
47 |
48 | Listen for requests at the given host and port. Use a `host` value of
49 | "0.0.0.0" to listen on all network interfaces, or "127.0.0.1" to only listen
50 | for requests from your own computer.
51 |
52 | Each request will be passed to `callback` in a distinct coroutine. The
53 | callback should return a table containing the response status, headers, and
54 | body. Only the status is required.
55 |
56 | ```lua
57 | http.listen('127.0.0.1', 80, function(request)
58 | return {
59 | status = 200,
60 | headers = { ['Content-Type'] = 'text/html' },
61 | body = '
Hello world!
'
62 | }
63 | end)
64 | ```
65 |
66 | Requests are tables containing the following keys:
67 |
68 | - `method`: An HTTP method, like 'GET' or 'POST'.
69 | - `url`: The URL from the raw HTTP request.
70 | - `path`: The path portion of the URL.
71 | - `query`: The query portion of the URL, as a string.
72 | - `headers`: The request headers, as a table.
73 | - `body`: The request body. Only present for POST requests.
74 |
75 | Note that the query string is not parsed. See the [url](url.md) module.
76 |
77 | ### http.format_date(time)
78 |
79 | Format a date according to RFC 1123, which is the preferred date format in
80 | HTTP. `time` is a number representing a [Unix time]. It defaults to the current time, given by `os.time()`.
81 |
82 | ### http.parse_date(date_string)
83 |
84 | Parse a date string in any of the acceptable [HTTP date formats].
85 |
86 | [HTTP date formats]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
87 |
88 | [Unix time]: http://en.wikipedia.org/wiki/Unix_time
89 |
--------------------------------------------------------------------------------
/doc/loop.md:
--------------------------------------------------------------------------------
1 | API Reference - uv.loop
2 | =======================
3 |
4 | The `loop` module provides direct control over the libuv event loop.
5 |
6 | ### loop.run()
7 |
8 | Run the libuv event loop. This is only necessary if an I/O request was created
9 | in a coroutine without the event loop already running. Requests made outside a
10 | coroutine are performed synchronously. It returns when the last I/O request is
11 | finished.
12 |
13 | ```lua
14 | print(uv.fs.readfile('README.md'))
15 | ```
16 |
17 | In this example, we're not in a coroutine, so `fs.readfile` ran the event loop
18 | implicitly. There is no need to call `loop.run()`.
19 |
20 | ```lua
21 | coroutine.resume(coroutine.create(function()
22 | print(uv.fs.readfile('README.md'))
23 | end))
24 | loop.run()
25 | ```
26 |
27 | Here, we manually created a coroutine that called `fs.readfile`, which yielded
28 | while awaiting the result. The event loop is not running, so unless we called
29 | `loop.run()`, the program would exit without performing the I/O request and the
30 | coroutine would never resume.
31 |
32 | ### loop.alive()
33 |
34 | Check whether the libuv event loop is running.
35 |
36 | ### loop.stop()
37 |
38 | Stop the libuv event loop.
39 |
40 | ### loop.idle(callback)
41 |
42 | Call `callback` continuously while the event loop has nothing else to do.
43 |
44 | ### loop.yield(callback)
45 |
46 | Call `callback` each time Lua yields control to the event loop.
47 |
48 | ### loop.resume(callback)
49 |
50 | Call `callback` each time Lua resumes control from libuv.
51 |
--------------------------------------------------------------------------------
/doc/parallel.md:
--------------------------------------------------------------------------------
1 | API Reference - uv.parallel
2 | ===========================
3 |
4 | The `parallel` module contains functions for performing computation in
5 | parallel across multiple coroutines. Note that Lua coroutines are not
6 | preemptive. Only one coroutine can run at a time, but they yield to each other
7 | while waiting for I/O requests from `libuv`.
8 |
9 | ### parallel.map(inputs, callback)
10 |
11 | Map an array of inputs to an array of outputs. Each input is passed to
12 | `callback` in its own coroutine, so that I/O operations are performed in
13 | parallel.
14 |
15 | ```lua
16 | local requests = {
17 | { url = 'http://example.com/page1' },
18 | { url = 'http://example.com/page2' },
19 | }
20 | local responses = parallel.map(requests, http.request)
21 | ```
22 |
23 | ### parallel.range(n, callback)
24 |
25 | Call `callback` `n` times, each in its own coroutine. Like a parallel version
26 | of the `for` loop.
27 |
--------------------------------------------------------------------------------
/doc/process.md:
--------------------------------------------------------------------------------
1 | API Reference - uv.process
2 | ==========================
3 |
4 | The `process` module provides functions for managing processes.
5 |
6 | ### process.spawn(args)
7 |
8 | Spawn a child process. `args` should be a table containing the executable path
9 | at index 1, arguments at indexes 2+, and any of the following options:
10 |
11 | - `env`: A table of environment variables as key/value pairs. If `env` is
12 | omitted, the child process will inherit the parent's environment.
13 |
14 | - `cwd`: Current working directory of the child process.
15 |
16 | - `stdin`: File descriptor to inherit as stdin. 0 causes it to inherit the
17 | parent's stdin.
18 |
19 | - `stdout`: File descriptor to inherit as stdout. 1 causes it to inherit the
20 | parent's stdout.
21 |
22 | - `stderr`: File descriptor to inherit as stderr. 2 causes it to inherit the
23 | parent's stderr.
24 |
25 | - `uid`: User ID under which to run.
26 |
27 | - `gid`: Group ID under which to run.
28 |
29 | Returns the signal that terminated the child process, or 0 on successful exit.
30 |
31 | ```lua
32 | local signal = process.spawn { '/bin/echo', 'Hello', 'world' }
33 |
34 | local file = fs.open('out.txt', 'w')
35 | process.spawn { '/bin/ls', cwd = fs.cwd(), stdout = file.descriptor }
36 | ```
37 |
38 | ### process.pid()
39 |
40 | Returns the current process's PID.
41 |
42 | ### process.path()
43 |
44 | Returns the path to the executable for the current process.
45 |
46 | ### process.kill(pid, signal)
47 |
48 | Send a signal to a process. `signal` defaults to "SIGKILL" and can have any of
49 | the following values:
50 |
51 | - "SIGKILL": The process should exit.
52 | - "SIGINT": The user pressed Control+C.
53 | - "SIGHUP": The user closed the console window.
54 | - "SIGWINCH": The user resized the console window.
55 |
56 | ### process.on(signal, callback)
57 |
58 | Call `callback` when a given signal is received. `signal` can only be "SIGINT", "SIGHUP", or "SIGWINCH".
59 |
60 | ```lua
61 | process.on('SIGINT', function()
62 | print('Shutting down...')
63 | os.exit()
64 | end)
65 | ```
66 |
67 | ### process.usage()
68 |
69 | Returns a table describing the current process's resource usage with the
70 | following keys:
71 |
72 | - `utime`: User CPU time used, in microseconds.
73 | - `stime`: System CPU time used, in microseconds.
74 | - `maxrss`: Maximum resident set size.
75 | - `ixrss`: Integral shared memory size.
76 | - `idrss`: Integral unshared data size.
77 | - `isrss`: Integral unshared stack size.
78 | - `minflt`: Page reclaims (soft page faults).
79 | - `majflt`: Page faults (hard page faults).
80 | - `nswap`: Swaps.
81 | - `inblock`: Block input operations.
82 | - `oublock`: Block output operations.
83 | - `msgsnd`: IPC messages sent.
84 | - `msgrcv`: IPC messages received.
85 | - `nsignals`: Signals received.
86 | - `nvcsw`: Voluntary context switches.
87 | - `nivcsw`: Involuntary context switches.
88 |
89 | ### process.title(value)
90 |
91 | Change the current process's title to `value`, if present. Returns the
92 | existing title. I think this only works on Windows.
93 |
--------------------------------------------------------------------------------
/doc/system.md:
--------------------------------------------------------------------------------
1 | API Reference - uv.system
2 | =========================
3 |
4 | The `system` module provides functionality related to the system as a whole,
5 | not just this process.
6 |
7 | ### system.free_memory()
8 |
9 | Returns the amount of free memory available to the system, in bytes.
10 |
11 | ### system.total_memory()
12 |
13 | Returns the total amount of memory in the system, in bytes.
14 |
15 | ### system.hrtime()
16 |
17 | Returns a high-resolution time in seconds. It is useful for measuring
18 | intervals but not for determining the current clock time.
19 |
20 | ### system.loadavg()
21 |
22 | Returns the system load average over 1, 5, and 15 minutes. The load average is
23 | the average number of jobs in the run queue.
24 |
25 | ### system.uptime()
26 |
27 | Returns the number of seconds since the system booted.
28 |
--------------------------------------------------------------------------------
/doc/tcp.md:
--------------------------------------------------------------------------------
1 | API Reference - uv.tcp
2 | =======================
3 |
4 | The `tcp` module provides both a client and a server.
5 |
6 | ### tcp.listen(host, port)
7 |
8 | Start listening for TCP connections. Returns a server object. Call
9 | `server:accept()` to accept a new connection and `server:close()` to stop
10 | listening.
11 |
12 | ```lua
13 | local server = tcp.listen('127.0.0.1', 7000)
14 | while true do
15 | local socket = server:accept()
16 | while true do
17 | local data = socket:read()
18 | if data:find('quit') then
19 | break
20 | end
21 | socket:write(data)
22 | end
23 | socket:close()
24 | end
25 | ```
26 |
27 | ### tcp.connect(host, port)
28 |
29 | Connect to a TCP server. Returns a connection with `read`, `write`, and
30 | `close` methods.
31 |
32 | ```lua
33 | local socket = tcp.connect('127.0.0.1', 7000)
34 | socket:write('ping')
35 | local data = socket:read()
36 | socket:write('quit')
37 | socket:close()
38 | ```
39 |
--------------------------------------------------------------------------------
/doc/timer.md:
--------------------------------------------------------------------------------
1 | API Reference - uv.timer
2 | ========================
3 |
4 | ### timer.set(timeout, callback)
5 |
6 | Schedule a function to be called once in the future. Returns immediately.
7 |
8 | ```lua
9 | timer.set(5000, function()
10 | print('Ding!')
11 | end)
12 | print('Waiting 5 seconds...')
13 | loop.run()
14 | print('The timer dinged.')
15 | ```
16 |
17 | ### timer.every(timeout, callback)
18 |
19 | Schedule a function to be called every `timeout` milliseconds. Returns
20 | immediately.
21 |
22 | ```lua
23 | timer.every(1000, function(t)
24 | print('Tick...')
25 | if we_are_done then
26 | t:stop()
27 | end
28 | end)
29 | loop.run()
30 | ```
31 |
32 | ### timer.sleep(timeout, callback)
33 |
34 | Yield the current coroutine for `timeout` milliseconds.
35 |
36 | ```lua
37 | print('Going to sleep...')
38 | timer.sleep(5000)
39 | print('Woke up')
40 | ```
41 |
--------------------------------------------------------------------------------
/doc/url.md:
--------------------------------------------------------------------------------
1 | API Reference - uv.url
2 | ======================
3 |
4 | The `url` module provides functions for working with URLs.
5 |
6 | ### url.split(str)
7 |
8 | Return a URL's components as a table. The table may contain any of the
9 | following keys, depending on which are present in the URL:
10 |
11 | - `scheme`
12 | - `userinfo`
13 | - `host`
14 | - `port`
15 | - `path`
16 | - `query`
17 | - `fragment`
18 |
19 | ```lua
20 | local parts = url.split 'http://myname:12345@host.com:80/path?a=1&b=2#section
21 |
22 | -- parts.scheme == 'http'
23 | -- parts.userinfo == 'myname:12345'
24 | -- parts.host == 'host.com'
25 | -- parts.port == '80'
26 | -- parts.path == '/path'
27 | -- parts.query == 'a=1&b=2'
28 | -- parts.fragment == 'section'
29 | ```
30 |
31 | ### url.join(parts)
32 |
33 | The inverse of `url.split`. It takes a table of URL components and returns the
34 | assembled URL as a string.
35 |
36 | ```lua
37 | url.join { path = '/path', query = 'a=1&b=2' }
38 | -- '/path?a=1&b=2'
39 | ```
40 |
41 | ### url.encode(value)
42 |
43 | Encode a value as a URI component. If `value` is a table, `url.encode` will return a full query string.
44 |
45 | ```lua
46 | local query = url.encode { name = 'Isaac Newton' }
47 | -- query == 'name=Isaac%20Newton'
48 | ```
49 |
50 | ### url.relative(base, relative)
51 |
52 | Evaluate a relative URL in the context of a base URL. It mirrors the logic
53 | applied by browsers when evaluating a link in a web page.
54 |
55 | ```lua
56 | url.relative('http://host.com/path/to/page', 'other/page')
57 | -- 'http://host.com/path/to/other/page'
58 | ```
59 |
--------------------------------------------------------------------------------
/src/uv.lua:
--------------------------------------------------------------------------------
1 | return require 'uv/init'
2 |
--------------------------------------------------------------------------------
/src/uv/ctypes/init.lua:
--------------------------------------------------------------------------------
1 | return {
2 | uv_buf_t = require 'uv/ctypes/uv_buf_t',
3 | uv_check_t = require 'uv/ctypes/uv_check_t',
4 | uv_connect_t = require 'uv/ctypes/uv_connect_t',
5 | uv_fs_t = require 'uv/ctypes/uv_fs_t',
6 | uv_getaddrinfo_t = require 'uv/ctypes/uv_getaddrinfo_t',
7 | uv_handle_t = require 'uv/ctypes/uv_handle_t',
8 | uv_idle_t = require 'uv/ctypes/uv_idle_t',
9 | uv_loop_t = require 'uv/ctypes/uv_loop_t',
10 | uv_prepare_t = require 'uv/ctypes/uv_prepare_t',
11 | uv_process_t = require 'uv/ctypes/uv_process_t',
12 | uv_process_options_t = require 'uv/ctypes/uv_process_options_t',
13 | uv_signal_t = require 'uv/ctypes/uv_signal_t',
14 | uv_stream_t = require 'uv/ctypes/uv_stream_t',
15 | uv_tcp_t = require 'uv/ctypes/uv_tcp_t',
16 | uv_timer_t = require 'uv/ctypes/uv_timer_t',
17 | uv_write_t = require 'uv/ctypes/uv_write_t',
18 | }
19 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_buf_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local ctype = require 'uv/util/ctype'
3 | local libuv = require 'uv/libuv'
4 | local libuv2 = require 'uv/libuv2'
5 | local libc = require 'uv/libc'
6 |
7 | --------------------------------------------------------------------------------
8 | -- uv_buf_t
9 | --------------------------------------------------------------------------------
10 |
11 | local uv_buf_t = ctype('uv_buf_t', function(base, len)
12 | local self = ffi.cast('uv_buf_t*', libc.malloc(ffi.sizeof('uv_buf_t')))
13 | self.len = len or 65536
14 | self.base = libc.malloc(self.len)
15 | if base then
16 | assert(#base <= self.len)
17 | ffi.copy(self.base, base, #base)
18 | end
19 | return self
20 | end)
21 |
22 | function uv_buf_t:free()
23 | libc.free(self.base)
24 | libc.free(self)
25 | end
26 |
27 | return uv_buf_t
28 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_check_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local async = require 'uv/util/async'
3 | local ctype = require 'uv/util/ctype'
4 | local libuv = require 'uv/libuv'
5 | local libuv2 = require 'uv/libuv2'
6 | local libc = require 'uv/libc'
7 | local verify = require 'uv/util/verify'
8 |
9 | local uv_check_t = ctype('uv_check_t', function(loop)
10 | loop = loop or libuv.uv_default_loop()
11 | local self = ffi.cast('uv_check_t*', libc.malloc(ffi.sizeof('uv_check_t')))
12 | verify(libuv.uv_check_init(loop, self))
13 | return self
14 | end)
15 |
16 | function uv_check_t:start(callback)
17 | verify(libuv.uv_check_start(self, async.uv_check_cb))
18 | while true do
19 | async.yield(self)
20 | callback()
21 | end
22 | end
23 |
24 | return uv_check_t
25 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_connect_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local ctype = require 'uv/util/ctype'
3 | local libuv = require 'uv/libuv'
4 | local libuv2 = require 'uv/libuv2'
5 | local libc = require 'uv/libc'
6 |
7 | --------------------------------------------------------------------------------
8 | -- uv_connect_t
9 | --------------------------------------------------------------------------------
10 |
11 | local uv_connect_t = ctype('uv_connect_t', function()
12 | local self = ffi.cast('uv_connect_t*', libc.malloc(ffi.sizeof('uv_connect_t')))
13 | return self
14 | end)
15 |
16 | function uv_connect_t:free()
17 | libc.free(self)
18 | end
19 |
20 | return uv_connect_t
21 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_fs_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local async = require 'uv/util/async'
3 | local ctype = require 'uv/util/ctype'
4 | local libuv = require 'uv/libuv'
5 | local libuv2 = require 'uv/libuv2'
6 | local libc = require 'uv/libc'
7 | local uv_buf_t = require 'uv/ctypes/uv_buf_t'
8 | local uv_loop_t = require 'uv/ctypes/uv_loop_t'
9 | local errno = require 'uv/util/errno'
10 | local verify = require 'uv/util/verify'
11 |
12 | --------------------------------------------------------------------------------
13 | -- uv_fs_t
14 | --------------------------------------------------------------------------------
15 |
16 | local uv_fs_t = ctype('uv_fs_t', function(loop)
17 | local self = ffi.cast('uv_fs_t*', libc.malloc(ffi.sizeof('uv_fs_t')))
18 | self.loop = loop or libuv.uv_default_loop()
19 | return self
20 | end)
21 |
22 | function uv_fs_t:open(path, flags, mode)
23 | verify(libuv2.uv2_fs_open(self.loop, self, path, flags, mode, async.uv_fs_cb))
24 | async.yield(self)
25 | local descriptor = tonumber(self.result)
26 | if descriptor < 0 then
27 | error(errno[tonumber(self.result)])
28 | end
29 | libuv.uv_fs_req_cleanup(self)
30 | return descriptor
31 | end
32 |
33 | function uv_fs_t:read(file)
34 | local buf = uv_buf_t()
35 | verify(libuv.uv_fs_read(self.loop, self, file, buf, 1, -1, async.uv_fs_cb))
36 | async.yield(self)
37 | local nread = tonumber(self.result)
38 | if nread < 0 then
39 | error(errno[tonumber(self.result)])
40 | end
41 | local chunk = ffi.string(buf.base, nread)
42 | buf:free()
43 | libuv.uv_fs_req_cleanup(self)
44 | return chunk
45 | end
46 |
47 | function uv_fs_t:close(file)
48 | verify(libuv.uv_fs_close(self.loop, self, file, async.uv_fs_cb))
49 | async.yield(self)
50 | local status = tonumber(self.result)
51 | if status < 0 then
52 | error(errno[tonumber(self.result)])
53 | end
54 | libuv.uv_fs_req_cleanup(self)
55 | end
56 |
57 | function uv_fs_t:unlink(path)
58 | verify(libuv.uv_fs_unlink(self.loop, self, path, async.uv_fs_cb))
59 | async.yield(self)
60 | local status = tonumber(self.result)
61 | if status < 0 then
62 | error(errno[tonumber(self.result)])
63 | end
64 | libuv.uv_fs_req_cleanup(self)
65 | end
66 |
67 | function uv_fs_t:write(file, buffer)
68 | local buf = uv_buf_t(buffer, #buffer)
69 | verify(libuv.uv_fs_write(self.loop, self, file, buf, 1, -1, async.uv_fs_cb))
70 | async.yield(self)
71 | buf:free()
72 | local status = tonumber(self.result)
73 | if status < 0 then
74 | error(errno[tonumber(self.result)])
75 | end
76 | libuv.uv_fs_req_cleanup(self)
77 | end
78 |
79 | function uv_fs_t:mkdir(path, mode)
80 | verify(libuv.uv_fs_mkdir(self.loop, self, path, mode, async.uv_fs_cb))
81 | async.yield(self)
82 | local status = tonumber(self.result)
83 | if status < 0 then
84 | error(errno[tonumber(self.result)])
85 | end
86 | libuv.uv_fs_req_cleanup(self)
87 | end
88 |
89 | function uv_fs_t:rmdir(path)
90 | verify(libuv.uv_fs_rmdir(self.loop, self, path, async.uv_fs_cb))
91 | async.yield(self)
92 | local status = tonumber(self.result)
93 | if status < 0 then
94 | error(errno[tonumber(self.result)])
95 | end
96 | libuv.uv_fs_req_cleanup(self)
97 | end
98 |
99 | function uv_fs_t:chmod(path, mode)
100 | verify(libuv.uv_fs_chmod(self.loop, self, path, mode, async.uv_fs_cb))
101 | async.yield(self)
102 | local status = tonumber(self.result)
103 | if status < 0 then
104 | error(errno[tonumber(self.result)])
105 | end
106 | libuv.uv_fs_req_cleanup(self)
107 | end
108 |
109 | function uv_fs_t:fchmod(file, mode)
110 | verify(libuv.uv_fs_fchmod(self.loop, self, file, mode, async.uv_fs_cb))
111 | async.yield(self)
112 | local status = tonumber(self.result)
113 | if status < 0 then
114 | error(errno[tonumber(self.result)])
115 | end
116 | libuv.uv_fs_req_cleanup(self)
117 | end
118 |
119 | function uv_fs_t:chown(path, uid, gid)
120 | verify(libuv.uv_fs_chown(self.loop, self, path, uid, gid, async.uv_fs_cb))
121 | async.yield(self)
122 | local status = tonumber(self.result)
123 | if status < 0 then
124 | error(errno[tonumber(self.result)])
125 | end
126 | libuv.uv_fs_req_cleanup(self)
127 | end
128 |
129 | function uv_fs_t:fchown(file, uid, gid)
130 | verify(libuv.uv_fs_fchown(self.loop, self, file, uid, gid, async.uv_fs_cb))
131 | async.yield(self)
132 | local status = tonumber(self.result)
133 | if status < 0 then
134 | error(errno[tonumber(self.result)])
135 | end
136 | libuv.uv_fs_req_cleanup(self)
137 | end
138 |
139 | function uv_fs_t:stat(path)
140 | verify(libuv.uv_fs_stat(self.loop, self, path, async.uv_fs_cb))
141 | async.yield(self)
142 | local status = tonumber(self.result)
143 | if status < 0 then
144 | error(errno[tonumber(self.result)])
145 | end
146 | local stat = ffi.cast('uv_stat_t*', self.ptr)
147 | libuv.uv_fs_req_cleanup(self)
148 | return stat
149 | end
150 |
151 | function uv_fs_t:fstat(path)
152 | verify(libuv.uv_fs_fstat(self.loop, self, path, async.uv_fs_cb))
153 | async.yield(self)
154 | local status = tonumber(self.result)
155 | if status < 0 then
156 | error(errno[tonumber(self.result)])
157 | end
158 | local stat = ffi.cast('uv_stat_t*', self.ptr)
159 | libuv.uv_fs_req_cleanup(self)
160 | return stat
161 | end
162 |
163 | function uv_fs_t:lstat(path)
164 | verify(libuv.uv_fs_lstat(self.loop, self, path, async.uv_fs_cb))
165 | async.yield(self)
166 | local status = tonumber(self.result)
167 | if status < 0 then
168 | error(errno[tonumber(self.result)])
169 | end
170 | local stat = ffi.cast('uv_stat_t*', self.ptr)
171 | libuv.uv_fs_req_cleanup(self)
172 | return stat
173 | end
174 |
175 | function uv_fs_t:rename(path, new_path)
176 | verify(libuv.uv_fs_rename(self.loop, self, path, new_path, async.uv_fs_cb))
177 | async.yield(self)
178 | local status = tonumber(self.result)
179 | if status < 0 then
180 | error(errno[tonumber(self.result)])
181 | end
182 | libuv.uv_fs_req_cleanup(self)
183 | end
184 |
185 | function uv_fs_t:link(path, new_path)
186 | verify(libuv.uv_fs_link(self.loop, self, path, new_path, async.uv_fs_cb))
187 | async.yield(self)
188 | local status = tonumber(self.result)
189 | if status < 0 then
190 | error(errno[tonumber(self.result)])
191 | end
192 | libuv.uv_fs_req_cleanup(self)
193 | end
194 |
195 | function uv_fs_t:symlink(path, new_path, flags)
196 | local flags = flags or 0
197 | verify(libuv.uv_fs_symlink(self.loop, self, path, new_path, flags, async.uv_fs_cb))
198 | async.yield(self)
199 | local status = tonumber(self.result)
200 | if status < 0 then
201 | error(errno[tonumber(self.result)])
202 | end
203 | libuv.uv_fs_req_cleanup(self)
204 | end
205 |
206 | function uv_fs_t:readlink(path)
207 | verify(libuv.uv_fs_readlink(self.loop, self, path, async.uv_fs_cb))
208 | async.yield(self)
209 | local status = tonumber(self.result)
210 | if status < 0 then
211 | error(errno[tonumber(self.result)])
212 | end
213 | local path = ffi.string(self.ptr)
214 | libuv.uv_fs_req_cleanup(self)
215 | return path
216 | end
217 |
218 | function uv_fs_t:fsync(file)
219 | verify(libuv.uv_fs_fsync(self.loop, self, file, async.uv_fs_cb))
220 | async.yield(self)
221 | local status = tonumber(self.result)
222 | if status < 0 then
223 | error(errno[tonumber(self.result)])
224 | end
225 | libuv.uv_fs_req_cleanup(self)
226 | end
227 |
228 | local function cstrings(cstrings, count)
229 | cstrings = ffi.cast('char*', cstrings)
230 | local t = {}
231 | for i = 1, count do
232 | local s = ffi.string(cstrings)
233 | cstrings = cstrings + #s + 1
234 | t[i] = s
235 | end
236 | return t
237 | end
238 |
239 | function uv_fs_t:readdir(path, flags)
240 | verify(libuv.uv_fs_readdir(self.loop, self, path, flags, async.uv_fs_cb))
241 | async.yield(self)
242 | local status = tonumber(self.result)
243 | if status < 0 then
244 | error(errno[tonumber(self.result)])
245 | end
246 | local filenames = cstrings(self.ptr, status)
247 | libuv.uv_fs_req_cleanup(self)
248 | return filenames
249 | end
250 |
251 | return uv_fs_t
252 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_getaddrinfo_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local async = require 'uv/util/async'
3 | local ctype = require 'uv/util/ctype'
4 | local libuv = require 'uv/libuv'
5 | local libc = require 'uv/libc'
6 | local uv_loop_t = require 'uv/ctypes/uv_loop_t'
7 | local verify = require 'uv/util/verify'
8 |
9 | local AF_INET = 2
10 | local AF_INET6 = 28
11 |
12 | --------------------------------------------------------------------------------
13 | -- sockaddr_in
14 | --------------------------------------------------------------------------------
15 |
16 | local sockaddr_in = ctype('struct sockaddr_in')
17 |
18 | function sockaddr_in:ip()
19 | local buf = ffi.new('char[?]', 16)
20 | libuv.uv_ip4_name(self, buf, 16)
21 | return ffi.string(buf)
22 | end
23 |
24 | function sockaddr_in:port()
25 | return libc.ntohs(self.sin_port)
26 | end
27 |
28 | --------------------------------------------------------------------------------
29 | -- uv_getaddrinfo_t
30 | --------------------------------------------------------------------------------
31 |
32 | local uv_getaddrinfo_t = ctype('uv_getaddrinfo_t', function(loop)
33 | local self = ffi.cast('uv_getaddrinfo_t*', libc.malloc(ffi.sizeof('uv_getaddrinfo_t')))
34 | self.loop = loop or libuv.uv_default_loop()
35 | return self
36 | end)
37 |
38 | function uv_getaddrinfo_t:free()
39 | libc.free(self)
40 | end
41 |
42 | function uv_getaddrinfo_t:getaddrinfo(node, service)
43 | local hints = ffi.new('struct addrinfo')
44 | -- hints.ai_family = AF_INET
45 | verify(libuv.uv_getaddrinfo(self.loop, self, async.uv_getaddrinfo_cb, node, service, hints))
46 | local status, addrinfo = async.yield(self)
47 | verify(status)
48 | local addrs = {}
49 | local ai = addrinfo
50 | while ai ~= ffi.NULL do
51 | if ai.ai_addr.sa_family == AF_INET then
52 | local addr = ffi.new('struct sockaddr_in')
53 | ffi.copy(addr, ai.ai_addr, ai.ai_addrlen)
54 | -- print('addr: ', addr.sin_p)
55 | table.insert(addrs, addr)
56 | end
57 | ai = ai.ai_next
58 | end
59 | libuv.uv_freeaddrinfo(addrinfo)
60 | return addrs
61 | end
62 |
63 | return uv_getaddrinfo_t
64 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_handle_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local async = require 'uv/util/async'
3 | local ctype = require 'uv/util/ctype'
4 | local join = require 'uv/util/join'
5 | local libuv = require 'uv/libuv'
6 | local libuv2 = require 'uv/libuv2'
7 | local libc = require 'uv/libc'
8 | local verify = require 'uv/util/verify'
9 |
10 | local uv_handle_t = ctype('uv_handle_t')
11 |
12 | function uv_handle_t:close()
13 | -- libuv.uv_unref(self)
14 | libuv.uv_close(self, async.uv_close_cb)
15 | async.yield(self)
16 | end
17 |
18 | local type_itoa = {
19 | [libuv.UV_UNKNOWN_HANDLE] = 'unknown_handle',
20 | [libuv.UV_ASYNC] = 'async',
21 | [libuv.UV_CHECK] = 'check',
22 | [libuv.UV_FS_EVENT] = 'fs_event',
23 | [libuv.UV_FS_POLL] = 'fs_poll',
24 | [libuv.UV_HANDLE] = 'handle',
25 | [libuv.UV_IDLE] = 'idle',
26 | [libuv.UV_NAMED_PIPE] = 'named_pipe',
27 | [libuv.UV_POLL] = 'poll',
28 | [libuv.UV_PREPARE] = 'prepare',
29 | [libuv.UV_PROCESS] = 'process',
30 | [libuv.UV_STREAM] = 'stream',
31 | [libuv.UV_TCP] = 'tcp',
32 | [libuv.UV_TIMER] = 'timer',
33 | [libuv.UV_TTY] = 'tty',
34 | [libuv.UV_UDP] = 'udp',
35 | [libuv.UV_SIGNAL] = 'signal',
36 | [libuv.UV_FILE] = 'file',
37 | }
38 |
39 | function uv_handle_t:typestring()
40 | return type_itoa[tonumber(self.type)] or 'unknown'
41 | end
42 |
43 | function uv_handle_t:is_active()
44 | return 0 ~= libuv.uv_is_active(self)
45 | end
46 |
47 | return uv_handle_t
48 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_idle_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local async = require 'uv/util/async'
3 | local ctype = require 'uv/util/ctype'
4 | local libuv = require 'uv/libuv'
5 | local libuv2 = require 'uv/libuv2'
6 | local libc = require 'uv/libc'
7 | local verify = require 'uv/util/verify'
8 |
9 | local uv_idle_t = ctype('uv_idle_t', function(loop)
10 | local self = ffi.cast('uv_idle_t*', libc.malloc(ffi.sizeof('uv_idle_t')))
11 | verify(libuv.uv_idle_init(loop or libuv.uv_default_loop(), self))
12 | return self
13 | end)
14 |
15 | function uv_idle_t:start(callback)
16 | verify(libuv.uv_idle_start(self, async.uv_idle_cb))
17 | while true do
18 | async.yield(self)
19 | callback()
20 | end
21 | end
22 |
23 | return uv_idle_t
24 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_loop_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local ctype = require 'uv/util/ctype'
3 | local join = require 'uv/util/join'
4 | local libuv = require 'uv/libuv'
5 | local verify = require 'uv/util/verify'
6 |
7 | --------------------------------------------------------------------------------
8 | -- uv_loop_t
9 | --------------------------------------------------------------------------------
10 |
11 | local uv_loop_t = ctype('uv_loop_t')
12 |
13 | function uv_loop_t:run()
14 | verify(libuv.uv_run(self, libuv.UV_RUN_DEFAULT))
15 | end
16 |
17 | function uv_loop_t:stop()
18 | libuv.uv_stop(self)
19 | end
20 |
21 | function uv_loop_t:close()
22 | libuv.uv_loop_close(self)
23 | end
24 |
25 | function uv_loop_t:walk(callback)
26 | local callback = ffi.cast('uv_walk_cb', callback)
27 | libuv.uv_walk(self, callback, nil)
28 | callback:free()
29 | end
30 |
31 | function uv_loop_t:now()
32 | return libuv.uv_now(self)
33 | end
34 |
35 | return uv_loop_t
36 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_prepare_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local async = require 'uv/util/async'
3 | local ctype = require 'uv/util/ctype'
4 | local libuv = require 'uv/libuv'
5 | local libuv2 = require 'uv/libuv2'
6 | local libc = require 'uv/libc'
7 | local verify = require 'uv/util/verify'
8 |
9 | local uv_prepare_t = ctype('uv_prepare_t', function(loop)
10 | local self = ffi.cast('uv_prepare_t*', libc.malloc(ffi.sizeof('uv_prepare_t')))
11 | verify(libuv.uv_prepare_init(loop or libuv.uv_default_loop(), self))
12 | return self
13 | end)
14 |
15 | function uv_prepare_t:start(callback)
16 | verify(libuv.uv_prepare_start(self, async.uv_prepare_cb))
17 | while true do
18 | async.yield(self)
19 | callback()
20 | end
21 | end
22 |
23 | return uv_prepare_t
24 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_process_options_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local async = require 'uv/util/async'
3 | local ctype = require 'uv/util/ctype'
4 | local libuv = require 'uv/libuv'
5 | local libuv2 = require 'uv/libuv2'
6 | local libc = require 'uv/libc'
7 |
8 | local uv_process_options_t = ctype('uv_process_options_t', function(loop)
9 | local self = ffi.cast('uv_process_options_t*', libc.malloc(ffi.sizeof('uv_process_options_t')))
10 | ffi.fill(self, ffi.sizeof('uv_process_options_t'), 0)
11 | return self
12 | end)
13 |
14 | function uv_process_options_t:free()
15 | libc.free(self)
16 | end
17 |
18 | return uv_process_options_t
19 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_process_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local async = require 'uv/util/async'
3 | local ctype = require 'uv/util/ctype'
4 | local libuv = require 'uv/libuv'
5 | local libuv2 = require 'uv/libuv2'
6 | local libc = require 'uv/libc'
7 | local verify = require 'uv/util/verify'
8 |
9 | local uv_process_t = ctype('uv_process_t', function(loop)
10 | local self = ffi.cast('uv_process_t*', libc.malloc(ffi.sizeof('uv_process_t')))
11 | self.loop = loop or libuv.uv_default_loop()
12 | return self
13 | end)
14 |
15 | function uv_process_t:spawn(options)
16 | verify(libuv.uv_spawn(self.loop, self, options))
17 | local exit_status, term_signal = async.yield(self)
18 | verify(exit_status)
19 | return term_signal
20 | end
21 |
22 | function uv_process_t:kill(signum)
23 | verify(libuv.uv_process_kill(self, signum))
24 | end
25 |
26 | return uv_process_t
27 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_signal_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local async = require 'uv/util/async'
3 | local ctype = require 'uv/util/ctype'
4 | local libuv = require 'uv/libuv'
5 | local libuv2 = require 'uv/libuv2'
6 | local libc = require 'uv/libc'
7 | local verify = require 'uv/util/verify'
8 |
9 | --------------------------------------------------------------------------------
10 | -- uv_signal_t
11 | --------------------------------------------------------------------------------
12 |
13 | local uv_signal_t = ctype('uv_signal_t', function(loop)
14 | local self = ffi.cast('uv_signal_t*', libc.malloc(ffi.sizeof('uv_signal_t')))
15 | verify(libuv.uv_signal_init(loop or libuv.uv_default_loop(), self))
16 | return self
17 | end)
18 |
19 | function uv_signal_t:start(signum, callback)
20 | verify(libuv.uv_signal_start(self, async.uv_signal_cb, signum))
21 | while true do
22 | local signum = async.yield(self)
23 | callback()
24 | end
25 | end
26 |
27 | function uv_signal_t:stop()
28 | verify(libuv.uv_signal_stop(self))
29 | end
30 |
31 | function uv_signal_t:free()
32 | libc.free(self)
33 | end
34 |
35 | return uv_signal_t
36 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_stream_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local async = require 'uv/util/async'
3 | local ctype = require 'uv/util/ctype'
4 | local libuv = require 'uv/libuv'
5 | local libuv2 = require 'uv/libuv2'
6 | local libc = require 'uv/libc'
7 | local uv_buf_t = require 'uv/ctypes/uv_buf_t'
8 | local uv_write_t = require 'uv/ctypes/uv_write_t'
9 | local uv_loop_t = require 'uv/ctypes/uv_loop_t'
10 | local verify = require 'uv/util/verify'
11 |
12 | --------------------------------------------------------------------------------
13 | -- uv_stream_t
14 | --------------------------------------------------------------------------------
15 |
16 | local uv_stream_t = ctype('uv_stream_t')
17 |
18 | function uv_stream_t:read()
19 | libuv.uv_read_start(self, libuv2.uv2_alloc_cb, async.uv_read_cb)
20 | local nread, buf = async.yield(self)
21 | libuv.uv_read_stop(self)
22 | verify(nread)
23 | local chunk = (nread < 0) and '' or ffi.string(buf.base, nread)
24 | libc.free(buf.base)
25 | return chunk, nread
26 | end
27 |
28 | function uv_stream_t:write(content)
29 | local req = uv_write_t()
30 | local buf = uv_buf_t(content, #content)
31 | verify(libuv.uv_write(req, self, buf, 1, async.uv_write_cb))
32 | verify(async.yield(req))
33 | req:free()
34 | buf:free()
35 | end
36 |
37 | function uv_stream_t:close()
38 | libuv2.uv2_stream_close(self, async.uv_close_cb)
39 | async.yield(self)
40 | end
41 |
42 | return uv_stream_t
43 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_tcp_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local async = require 'uv/util/async'
3 | local ctype = require 'uv/util/ctype'
4 | local join = require 'uv/util/join'
5 | local libuv = require 'uv/libuv'
6 | local libuv2 = require 'uv/libuv2'
7 | local libc = require 'uv/libc'
8 | local uv_buf_t = require 'uv/ctypes/uv_buf_t'
9 | local uv_connect_t = require 'uv/ctypes/uv_connect_t'
10 | local uv_getaddrinfo_t = require 'uv/ctypes/uv_getaddrinfo_t'
11 | local uv_loop_t = require 'uv/ctypes/uv_loop_t'
12 | local verify = require 'uv/util/verify'
13 |
14 | --------------------------------------------------------------------------------
15 | -- uv_tcp_t
16 | --------------------------------------------------------------------------------
17 |
18 | local uv_tcp_t = ctype('uv_tcp_t', function(loop)
19 | local self = ffi.cast('uv_tcp_t*', libc.malloc(ffi.sizeof('uv_tcp_t')))
20 | libuv.uv_tcp_init(loop or libuv.uv_default_loop(), self)
21 | return self
22 | end)
23 |
24 | function uv_tcp_t:bind(host, port)
25 | local addr = ffi.new('struct sockaddr_in')
26 | libuv.uv_ip4_addr(host, port, addr)
27 | addr = ffi.cast('struct sockaddr*', addr)
28 | libuv.uv_tcp_bind(self, addr, 0)
29 | end
30 |
31 | function uv_tcp_t:connect(host, port)
32 | local socket = uv_tcp_t(self.loop)
33 | local connect = uv_connect_t()
34 | local addr = ffi.new('struct sockaddr_in')
35 | if libuv.uv_ip4_addr(host, port, addr) ~= 0 then
36 | local gai = uv_getaddrinfo_t(self.loop)
37 | addr = gai:getaddrinfo(host, tostring(port))[1]
38 | gai:free()
39 | end
40 | addr = ffi.cast('struct sockaddr*', addr)
41 |
42 | verify(libuv.uv_tcp_connect(connect, socket, addr, async.uv_connect_cb))
43 | local status = async.yield(connect)
44 | if status < 0 then
45 | verify(status)
46 | end
47 | local handle = connect.handle
48 | connect:free()
49 | return handle
50 | end
51 |
52 | function uv_tcp_t:read()
53 | libuv2.uv2_tcp_read_start(self, libuv2.uv2_alloc_cb, async.uv_read_cb)
54 | local nread, buf = async.yield(self)
55 | libuv2.uv2_tcp_read_stop(self)
56 | verify(nread)
57 | local chunk = (nread < 0) and '' or ffi.string(buf.base, nread)
58 | libc.free(buf.base)
59 | return chunk, nread
60 | end
61 |
62 | function uv_tcp_t:write(content)
63 | local req = ffi.new('uv_write_t')
64 | local buf = uv_buf_t(content, #content)
65 | verify(libuv2.uv2_tcp_write(req, self, buf, 1, async.uv_write_cb))
66 | local status = async.yield(req)
67 | buf:free()
68 | return 0 == status
69 | end
70 |
71 | function uv_tcp_t:listen()
72 | verify(libuv2.uv2_tcp_listen(self, 128, async.uv_connection_cb))
73 | end
74 |
75 | function uv_tcp_t:accept()
76 | local status = async.yield(self)
77 | if status >= 0 then
78 | local socket = uv_tcp_t()
79 | if 0 == tonumber(libuv2.uv2_tcp_accept(self, socket)) then
80 | return socket
81 | end
82 | end
83 | end
84 |
85 | function uv_tcp_t:close()
86 | libuv2.uv2_tcp_close(self, async.uv_close_cb)
87 | async.yield(self)
88 | libc.free(self)
89 | end
90 |
91 | function uv_tcp_t:getsockname()
92 | local addr = ffi.new('struct sockaddr')
93 | local len = ffi.new('int[1]')
94 | len[0] = ffi.sizeof(addr)
95 | local buf = libc.malloc(4096)
96 | verify(libuv.uv_tcp_getsockname(self, addr, len))
97 | addr = ffi.cast('struct sockaddr_in*', addr)
98 | verify(libuv.uv_ip4_name(addr, buf, 4096))
99 | local peername = ffi.string(buf)
100 | libc.free(buf)
101 | return peername
102 | end
103 |
104 | function uv_tcp_t:getpeername()
105 | local addr = ffi.new('struct sockaddr')
106 | local len = ffi.new('int[1]')
107 | len[0] = ffi.sizeof(addr)
108 | local buf = libc.malloc(4096)
109 | verify(libuv.uv_tcp_getpeername(self, addr, len))
110 | addr = ffi.cast('struct sockaddr_in*', addr)
111 | verify(libuv.uv_ip4_name(addr, buf, 4096))
112 | local peername = ffi.string(buf)
113 | libc.free(buf)
114 | return peername
115 | end
116 |
117 | return uv_tcp_t
118 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_timer_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local async = require 'uv/util/async'
3 | local ctype = require 'uv/util/ctype'
4 | local libuv = require 'uv/libuv'
5 | local libc = require 'uv/libc'
6 | local uv_loop_t = require 'uv/ctypes/uv_loop_t'
7 | local verify = require 'uv/util/verify'
8 |
9 | --------------------------------------------------------------------------------
10 | -- uv_timer_t
11 | --------------------------------------------------------------------------------
12 |
13 | local uv_timer_t = ctype('uv_timer_t', function(loop)
14 | local self = ffi.cast('uv_timer_t*', libc.malloc(ffi.sizeof('uv_timer_t')))
15 | libuv.uv_timer_init(loop or libuv.uv_default_loop(), self)
16 | return self
17 | end)
18 |
19 | function uv_timer_t:free()
20 | libc.free(self)
21 | end
22 |
23 | function uv_timer_t:every(timeout, callback)
24 | verify(libuv.uv_timer_start(self, async.uv_timer_cb, timeout, timeout))
25 | while true do
26 | callback(self, async.yield(self))
27 | end
28 | end
29 |
30 | function uv_timer_t:sleep(timeout)
31 | verify(libuv.uv_timer_start(self, async.uv_timer_cb, timeout, 0))
32 | async.yield(self)
33 | end
34 |
35 | function uv_timer_t:stop()
36 | verify(libuv.uv_timer_stop(self))
37 | end
38 |
39 | function uv_timer_t:again()
40 | verify(libuv.uv_timer_again(self))
41 | end
42 |
43 | function uv_timer_t:set_repeat(repeat_time)
44 | libuv.uv_timer_set_repeat(self, repeat_time)
45 | end
46 |
47 | function uv_timer_t:get_repeat()
48 | return libuv.uv_timer_get_repeat(self)
49 | end
50 |
51 | return uv_timer_t
52 |
--------------------------------------------------------------------------------
/src/uv/ctypes/uv_write_t.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local ctype = require 'uv/util/ctype'
3 | local libuv = require 'uv/libuv'
4 | local libuv2 = require 'uv/libuv2'
5 | local libc = require 'uv/libc'
6 |
7 | --------------------------------------------------------------------------------
8 | -- uv_write_t
9 | --------------------------------------------------------------------------------
10 |
11 | local uv_write_t = ctype('uv_write_t', function()
12 | local self = ffi.cast('uv_write_t*', libc.malloc(ffi.sizeof('uv_write_t')))
13 | return self
14 | end)
15 |
16 | function uv_write_t:free()
17 | libc.free(self)
18 | end
19 |
20 | return uv_write_t
21 |
--------------------------------------------------------------------------------
/src/uv/fs.lua:
--------------------------------------------------------------------------------
1 | require 'uv/ctypes/init'
2 | local class = require 'uv/util/class'
3 | local ffi = require 'ffi'
4 | local libuv = require 'uv/libuv'
5 | local libuv2 = require 'uv/libuv2'
6 | local libc = require 'uv/libc'
7 | local uv_fs_t = require 'uv/ctypes/uv_fs_t'
8 | local uv_buf_t = require 'uv/ctypes/uv_buf_t'
9 | local errno = require 'uv/util/errno'
10 |
11 | -- libc.umask(0)
12 |
13 |
14 | --------------------------------------------------------------------------------
15 | -- octal
16 | --------------------------------------------------------------------------------
17 |
18 | local function octal(s)
19 | local i = 0
20 | for c in s:gmatch('.') do
21 | i = i * 8 + tonumber(c)
22 | end
23 | return i
24 | end
25 |
26 | do
27 | assert(octal('0') == 0)
28 | assert(octal('1') == 1)
29 | assert(octal('10') == 8)
30 | assert(octal('11') == 9)
31 | assert(octal('100') == 64)
32 | assert(octal('101') == 65)
33 | end
34 |
35 | --------------------------------------------------------------------------------
36 | -- flags_atoi
37 | --------------------------------------------------------------------------------
38 |
39 | local flags_atoi = setmetatable({}, { __index = function(self, s)
40 | assert(type(s) == 'number', 'file flags should be "r", "w", "a", "r+", "w+", "a+", or a number')
41 | self[s] = s
42 | return s
43 | end})
44 |
45 | do
46 | local O_RDONLY = 0x0000 -- open for reading only
47 | local O_WRONLY = 0x0001 -- open for writing only
48 | local O_RDWR = 0x0002 -- open for reading and writing
49 | local O_NONBLOCK = 0x0004 -- no delay
50 | local O_APPEND = 0x0008 -- set append mode
51 | local O_SHLOCK = 0x0010 -- open with shared file lock
52 | local O_EXLOCK = 0x0020 -- open with exclusive file lock
53 | local O_ASYNC = 0x0040 -- signal pgrp when data ready
54 | local O_NOFOLLOW = 0x0100 -- don't follow symlinks
55 | local O_CREAT = 0x0200 -- create if nonexistant
56 | local O_TRUNC = 0x0400 -- truncate to zero length
57 | local O_EXCL = 0x0800 -- error if already exists
58 |
59 | flags_atoi['r'] = O_RDONLY
60 | flags_atoi['w'] = O_WRONLY + O_CREAT + O_TRUNC
61 | flags_atoi['a'] = O_WRONLY + O_CREAT + O_APPEND
62 | flags_atoi['r+'] = O_RDWR
63 | flags_atoi['w+'] = O_RDWR + O_CREAT + O_TRUNC
64 | flags_atoi['a+'] = O_RDWR + O_CREAT + O_APPEND
65 | end
66 |
67 | --------------------------------------------------------------------------------
68 | -- mode_atoi
69 | --------------------------------------------------------------------------------
70 |
71 | local mode_atoi = setmetatable({}, { __index = function(self, s)
72 | local i
73 | if type(s) == 'string' then
74 | if #s == 3 then
75 | i = octal(s)
76 | elseif #s == 9 then
77 | i = 0
78 | local function match_char(index, expected_char, n)
79 | local char = s:sub(index, index)
80 | if char == expected_char then
81 | i = i + n
82 | elseif char ~= '-' then
83 | error('file modes look like: "755" or "rwxr-xr-x"')
84 | end
85 | end
86 | match_char(1, 'r', 256)
87 | match_char(2, 'w', 128)
88 | match_char(3, 'x', 64)
89 | match_char(4, 'r', 32)
90 | match_char(5, 'w', 16)
91 | match_char(6, 'x', 8)
92 | match_char(7, 'r', 4)
93 | match_char(8, 'w', 2)
94 | match_char(9, 'x', 1)
95 | else
96 | error('file modes look like: "755" or "rwxr-xr-x"')
97 | end
98 | elseif type(s) == 'number' then
99 | i = s
100 | else
101 | error('unexpected mode type: ' .. type(s))
102 | end
103 | self[s] = i
104 | return i
105 | end})
106 |
107 | do
108 | assert(mode_atoi['001'] == 1)
109 | assert(mode_atoi['007'] == 7)
110 | assert(mode_atoi['070'] == 7 * 8)
111 | assert(mode_atoi['700'] == 7 * 64)
112 | assert(mode_atoi['777'] == 511)
113 |
114 | assert(mode_atoi['--------x'] == 1)
115 | assert(mode_atoi['-------w-'] == 2)
116 | assert(mode_atoi['------r--'] == 4)
117 | assert(mode_atoi['-----x---'] == 8)
118 | assert(mode_atoi['----w----'] == 16)
119 | assert(mode_atoi['---r-----'] == 32)
120 | assert(mode_atoi['--x------'] == 64)
121 | assert(mode_atoi['-w-------'] == 128)
122 | assert(mode_atoi['r--------'] == 256)
123 | assert(mode_atoi['rwxrwxrwx'] == 511)
124 |
125 | assert(mode_atoi[1] == 1)
126 | assert(mode_atoi[511] == 511)
127 |
128 | do
129 | local ok, err = pcall(function() return mode_atoi[true] end)
130 | assert(not ok)
131 | assert(err:find('unexpected mode type: boolean'))
132 | end
133 | end
134 |
135 | --------------------------------------------------------------------------------
136 | -- Stat
137 | --------------------------------------------------------------------------------
138 |
139 | local S_IFMT = octal('0170000') -- type of file mask
140 | local S_IFIFO = octal('0010000') -- named pipe (fifo)
141 | local S_IFCHR = octal('0020000') -- character special
142 | local S_IFDIR = octal('0040000') -- directory
143 | local S_IFBLK = octal('0060000') -- block special
144 | local S_IFREG = octal('0100000') -- regular
145 | local S_IFLNK = octal('0120000') -- symbolic link
146 | local S_IFSOCK = octal('0140000') -- socket
147 |
148 | local Stat = function(stat)
149 | return {
150 | uid = stat.st_uid,
151 | gid = stat.st_gid,
152 | size = stat.st_size,
153 | mode = bit.band(tonumber(stat.st_mode), bit.bnot(S_IFMT)),
154 | is_dir = bit.band(tonumber(stat.st_mode), S_IFDIR) > 0,
155 | is_fifo = bit.band(tonumber(stat.st_mode), S_IFIFO) > 0,
156 | atime = stat.st_atim.tv_sec,
157 | atimensec = stat.st_atim.tv_nsec,
158 | mtime = stat.st_mtim.tv_sec,
159 | mtimensec = stat.st_mtim.tv_nsec,
160 | ctime = stat.st_ctim.tv_sec,
161 | ctimensec = stat.st_ctim.tv_nsec,
162 | birthtime = stat.st_birthtim.tv_sec,
163 | birthtimensec = stat.st_birthtim.tv_nsec,
164 | }
165 | end
166 |
167 | --------------------------------------------------------------------------------
168 | -- File
169 | --------------------------------------------------------------------------------
170 |
171 | local File = class(function(descriptor)
172 | return { descriptor = descriptor }
173 | end)
174 |
175 | function File:read()
176 | return uv_fs_t():read(self.descriptor)
177 | end
178 |
179 | function File:close()
180 | return uv_fs_t():close(self.descriptor)
181 | end
182 |
183 | function File:write(buffer)
184 | return uv_fs_t():write(self.descriptor, buffer)
185 | end
186 |
187 | function File:chmod(mode)
188 | local mode = mode_atoi[mode or '700']
189 | return uv_fs_t():fchmod(self.descriptor, mode)
190 | end
191 |
192 | function File:chown(uid, gid)
193 | return uv_fs_t():fchown(self.descriptor, uid, gid)
194 | end
195 |
196 | function File:sync()
197 | return uv_fs_t():fsync(self.descriptor)
198 | end
199 |
200 | function File.stat()
201 | return Stat(uv_fs_t():fstat(self.descriptor))
202 | end
203 |
204 | --------------------------------------------------------------------------------
205 | -- fs
206 | --------------------------------------------------------------------------------
207 |
208 | local fs = {}
209 |
210 | function fs.open(path, flags, mode)
211 | local flags = flags_atoi[flags or 'r']
212 | local mode = mode_atoi[mode or '700']
213 | local mask = libc.umask(0)
214 | local descriptor = uv_fs_t():open(path, flags, mode)
215 | libc.umask(mask)
216 | return File(descriptor)
217 | end
218 |
219 | function fs.unlink(path)
220 | return uv_fs_t():unlink(path)
221 | end
222 |
223 | function fs.mkdir(path, mode)
224 | local mode = mode_atoi[mode or '700']
225 | return uv_fs_t():mkdir(path, mode)
226 | end
227 |
228 | function fs.rmdir(path)
229 | return uv_fs_t():rmdir(path)
230 | end
231 |
232 | function fs.chmod(path, mode)
233 | local mode = mode_atoi[mode or '700']
234 | return uv_fs_t():chmod(path, mode)
235 | end
236 |
237 | function fs.chown(path, uid, gid)
238 | return uv_fs_t():chown(path, uid, gid)
239 | end
240 |
241 | function fs.stat(path)
242 | return Stat(uv_fs_t():stat(path))
243 | end
244 |
245 | function fs.lstat(path)
246 | return uv_fs_t():lstat(path)
247 | end
248 |
249 | function fs.rename(path, new_path)
250 | return uv_fs_t():rename(path, new_path)
251 | end
252 |
253 | function fs.link(path, new_path)
254 | return uv_fs_t():link(path, new_path)
255 | end
256 |
257 | function fs.symlink(path, new_path)
258 | return uv_fs_t():symlink(path, new_path, 0)
259 | end
260 |
261 | function fs.readlink(path)
262 | return uv_fs_t():readlink(path)
263 | end
264 |
265 | function fs.readfile(path)
266 | local file = fs.open(path)
267 | local buffer = {}
268 | repeat
269 | local chunk = file:read()
270 | table.insert(buffer, chunk)
271 | until chunk == ''
272 | file:close()
273 | return table.concat(buffer)
274 | end
275 |
276 | function fs.writefile(path, body)
277 | local file = fs.open(path, 'w')
278 | file:write(body)
279 | file:close()
280 | end
281 |
282 | function fs.tmpname()
283 | return os.tmpname()
284 | end
285 |
286 | function fs.cwd()
287 | local buf = uv_buf_t()
288 | local status = libuv2.uv2_cwd(buf)
289 | if status ~= 0 then
290 | buf:free()
291 | error(errno(status))
292 | end
293 | local cwd = ffi.string(buf.base, buf.len)
294 | buf:free()
295 | return cwd
296 | end
297 |
298 | function fs.chdir(dir)
299 | local status = libuv.uv_chdir(dir)
300 | if 0 ~= status then
301 | error(errno(status))
302 | end
303 | end
304 |
305 | function fs.readdir(path)
306 | local filenames = uv_fs_t():readdir(path, 0)
307 | table.sort(filenames)
308 | return filenames
309 | end
310 |
311 | function fs.readdir_r(path)
312 | local filenames = {}
313 | local function scan(prefix)
314 | if fs.stat(path .. '/' .. prefix).is_dir then
315 | for _, f in ipairs(fs.readdir(path .. '/' .. prefix)) do
316 | scan(prefix .. '/' .. f)
317 | end
318 | else
319 | table.insert(filenames, prefix)
320 | end
321 | end
322 | for _, prefix in ipairs(fs.readdir(path)) do
323 | scan(prefix)
324 | end
325 | table.sort(filenames)
326 | return filenames
327 | end
328 |
329 | function fs.rm_rf(path)
330 | if fs.stat(path).is_dir then
331 | for _, filename in ipairs(fs.readdir(path)) do
332 | fs.rm_rf(path .. '/' .. filename)
333 | end
334 | fs.rmdir(path)
335 | else
336 | fs.unlink(path)
337 | end
338 | end
339 |
340 | function fs.extname(filename)
341 | return filename:match('%.[^./]+$') or ''
342 | end
343 |
344 | function fs.basename(filename)
345 | return filename:match('[^/]+$'):sub(1, -#fs.extname(filename) - 1)
346 | end
347 |
348 | function fs.dirname(filename)
349 | return filename:match('.*/') or ''
350 | end
351 |
352 | do
353 | assert(fs.dirname('/a/b') == '/a/')
354 | assert(fs.dirname('/a/') == '/a/')
355 | assert(fs.dirname('/a') == '/')
356 | assert(fs.dirname('/') == '/')
357 | assert(fs.dirname('a') == '')
358 | assert(fs.dirname('a/') == 'a/')
359 | assert(fs.dirname('a/b') == 'a/')
360 | end
361 |
362 | local function finally(try, always)
363 | local ok, err = xpcall(try, function(err)
364 | return debug.traceback(err, 2)
365 | end)
366 | always()
367 | if not ok then
368 | error(err, 0)
369 | end
370 | end
371 |
372 | function fs.with_tempdir(callback)
373 | local name = fs.tmpname()
374 | fs.unlink(name)
375 | fs.mkdir(name)
376 | finally(function()
377 | callback(name)
378 | end, function()
379 | fs.rm_rf(name)
380 | end)
381 | end
382 |
383 | return fs
384 |
--------------------------------------------------------------------------------
/src/uv/http.lua:
--------------------------------------------------------------------------------
1 | require 'uv/ctypes/init'
2 | local class = require 'uv/util/class'
3 | local ffi = require 'ffi'
4 | local libuv = require 'uv/libuv'
5 | local libuv2 = require 'uv/libuv2'
6 | local libhttp_parser = require 'uv/libhttp_parser'
7 | local uv_tcp_t = require 'uv/ctypes/uv_tcp_t'
8 | local url = require 'uv.url'
9 | local join = require 'uv/util/join'
10 |
11 | --------------------------------------------------------------------------------
12 | -- status_codes
13 | --------------------------------------------------------------------------------
14 |
15 | local status_codes = {}
16 | for i = 100, 199 do status_codes[i] = 'Informational' end
17 | for i = 200, 299 do status_codes[i] = 'Successful' end
18 | for i = 300, 399 do status_codes[i] = 'Redirection' end
19 | for i = 400, 499 do status_codes[i] = 'Client Error' end
20 | for i = 500, 599 do status_codes[i] = 'Successful' end
21 | status_codes[100] = 'Continue'
22 | status_codes[101] = 'Switching Protocols'
23 | status_codes[200] = 'OK'
24 | status_codes[201] = 'Created'
25 | status_codes[202] = 'Accepted'
26 | status_codes[203] = 'Non-Authoritative Information'
27 | status_codes[204] = 'No Content'
28 | status_codes[205] = 'Reset Content'
29 | status_codes[206] = 'Partial Content'
30 | status_codes[300] = 'Multiple Choices'
31 | status_codes[301] = 'Moved Permanently'
32 | status_codes[302] = 'Found'
33 | status_codes[303] = 'See Other'
34 | status_codes[304] = 'Not Modified'
35 | status_codes[305] = 'Use Proxy'
36 | status_codes[306] = '(Unused)'
37 | status_codes[307] = 'Temporary Redirect'
38 | status_codes[400] = 'Bad Request'
39 | status_codes[401] = 'Unauthorized'
40 | status_codes[402] = 'Payment Required'
41 | status_codes[403] = 'Forbidden'
42 | status_codes[404] = 'Not Found'
43 | status_codes[405] = 'Method Not Allowed'
44 | status_codes[406] = 'Not Acceptable'
45 | status_codes[407] = 'Proxy Authentication Required'
46 | status_codes[408] = 'Request Timeout'
47 | status_codes[409] = 'Conflict'
48 | status_codes[410] = 'Gone'
49 | status_codes[411] = 'Length Required'
50 | status_codes[412] = 'Precondition Failed'
51 | status_codes[413] = 'Request Entity Too Large'
52 | status_codes[414] = 'Request-URI Too Long'
53 | status_codes[415] = 'Unsupported Media Type'
54 | status_codes[416] = 'Requested Range Not Satisfiable'
55 | status_codes[417] = 'Expectation Failed'
56 | status_codes[500] = 'Internal Server Error'
57 | status_codes[501] = 'Not Implemented'
58 | status_codes[502] = 'Bad Gateway'
59 | status_codes[503] = 'Service Unavailable'
60 | status_codes[504] = 'Gateway Timeout'
61 | status_codes[505] = 'HTTP Version Not Supported'
62 |
63 | --------------------------------------------------------------------------------
64 | -- parse_http
65 | --------------------------------------------------------------------------------
66 |
67 | local function parse_http(stream, mode)
68 | local header_last, header_field, header_value, message_complete
69 | local headers, body, url = {}, ''
70 |
71 | local on = {}
72 |
73 | function on:url(buf, length)
74 | url = (url or '') .. ffi.string(buf, length)
75 | return 0
76 | end
77 |
78 | function on:header_field(buf, length)
79 | if header_last == 'field' then
80 | header_field = header_field .. ffi.string(buf, length)
81 | elseif header_last == 'value' then
82 | headers[header_field] = header_value
83 | header_field = ffi.string(buf, length)
84 | else
85 | header_field = ffi.string(buf, length)
86 | end
87 | header_last = 'field'
88 | return 0
89 | end
90 |
91 | function on:header_value(buf, length)
92 | if header_last == 'field' then
93 | header_value = ffi.string(buf, length)
94 | elseif header_last == 'value' then
95 | header_value = header_value .. ffi.string(buf, length)
96 | else
97 | error('header value before field')
98 | end
99 | header_last = 'value'
100 | return 0
101 | end
102 |
103 | function on:headers_complete()
104 | if header_last == 'value' then
105 | headers[header_field] = header_value
106 | end
107 | return 0
108 | end
109 |
110 | function on:body(buf, length)
111 | body = body .. ffi.string(buf, length)
112 | return 0
113 | end
114 |
115 | function on:message_complete()
116 | message_complete = true
117 | return 0
118 | end
119 |
120 | local settings = ffi.new('http_parser_settings')
121 |
122 | settings.on_url = ffi.cast('http_data_cb', on.url)
123 | settings.on_header_field = ffi.cast('http_data_cb', on.header_field)
124 | settings.on_header_value = ffi.cast('http_data_cb', on.header_value)
125 | settings.on_headers_complete = ffi.cast('http_cb', on.headers_complete)
126 | settings.on_body = ffi.cast('http_data_cb', on.body)
127 | settings.on_message_complete = ffi.cast('http_cb', on.message_complete)
128 |
129 | local parser = ffi.new('http_parser')
130 | libhttp_parser.http_parser_init(parser, mode)
131 |
132 | repeat
133 | libhttp_parser.http_parser_execute(parser, settings, stream:read())
134 | until message_complete
135 |
136 | settings.on_url:free()
137 | settings.on_header_field:free()
138 | settings.on_header_value:free()
139 | settings.on_headers_complete:free()
140 | settings.on_body:free()
141 | settings.on_message_complete:free()
142 |
143 | local message = { url = url, headers = headers, body = body }
144 |
145 | if mode == libhttp_parser.HTTP_REQUEST then
146 | message.method = ffi.string(libhttp_parser.http_method_str(parser.method))
147 | else
148 | message.status = parser.status_code
149 | end
150 |
151 | return message
152 | end
153 |
154 | do
155 | local function test_stream(s)
156 | local r = {}
157 | function r:read()
158 | function r:read() return '', 0 end
159 | return s, #s
160 | end
161 | return r
162 | end
163 |
164 | local request = parse_http(test_stream('GET / HTTP/1.1\n\n'), libhttp_parser.HTTP_REQUEST)
165 | assert(request.method == 'GET')
166 | assert(request.status == nil)
167 | assert(request.url == '/')
168 | assert(request.body == '')
169 |
170 | local request = parse_http(test_stream('POST / HTTP/1.1\n\n'), libhttp_parser.HTTP_REQUEST)
171 | assert(request.method == 'POST')
172 | end
173 |
174 | --------------------------------------------------------------------------------
175 | -- Connection
176 | --------------------------------------------------------------------------------
177 |
178 | local Connection = class(function(socket)
179 | return { socket = socket }
180 | end)
181 |
182 | function Connection:receive()
183 | local request = parse_http(self.socket, libhttp_parser.HTTP_REQUEST)
184 | url.split(request.url, request)
185 | request.ip = ffi.cast('uv_tcp_t*', self.socket):getpeername()
186 | return request
187 | end
188 |
189 | function Connection:respond(response)
190 | self.socket:write('HTTP/1.1 ' .. response.status .. ' ' .. status_codes[response.status] .. '\n')
191 |
192 | response.headers = response.headers or {}
193 |
194 | if not response.headers['Server'] then
195 | response.headers['Server'] = 'luajit-libuv'
196 | end
197 |
198 | response.headers['Content-Length'] = response.body and #response.body or 0
199 |
200 | for field, value in pairs(response.headers) do
201 | self.socket:write(field .. ': ' .. value .. '\n')
202 | end
203 |
204 | self.socket:write('\n')
205 |
206 | if response.body then
207 | self.socket:write(response.body)
208 | end
209 | end
210 |
211 | function Connection:close()
212 | return self.socket:close()
213 | end
214 |
215 | --------------------------------------------------------------------------------
216 | -- Server
217 | --------------------------------------------------------------------------------
218 |
219 | local Server = class(function(server)
220 | return { server = server }
221 | end)
222 |
223 | function Server:accept()
224 | local socket = self.server:accept()
225 | return Connection(socket)
226 | end
227 |
228 | function Server:close()
229 | return self.server:close()
230 | end
231 |
232 | --------------------------------------------------------------------------------
233 | -- http
234 | --------------------------------------------------------------------------------
235 |
236 | local http = {}
237 |
238 | function http.listen(host, port, on_request, on_error)
239 | if on_request then
240 | local server = http.listen(host, port)
241 | join(coroutine.create(function()
242 | while true do
243 | local connection = server:accept()
244 | join(coroutine.create(function()
245 | local request = connection:receive()
246 | local ok, response = xpcall(function()
247 | return on_request(request)
248 | end, on_error or error)
249 | if ok then
250 | connection:respond(response)
251 | end
252 | connection:close()
253 | end))
254 | end
255 | end))
256 | if coroutine.running() then
257 | return server
258 | else
259 | libuv.uv_default_loop():run()
260 | return
261 | end
262 | end
263 |
264 | local server = uv_tcp_t()
265 | server:bind(host, port)
266 | server:listen()
267 | return Server(server)
268 | end
269 |
270 | function http.request(request)
271 | if request.url then
272 | url.split(request.url, request)
273 | end
274 |
275 | local method = request.method or 'GET'
276 | local host = request.host or error('host required', 2)
277 | local port = request.port or 80
278 | local path = request.path or '/'
279 | local query = request.query or ''
280 | local headers = request.headers or {}
281 | local body = request.body or ''
282 |
283 | if not headers['Host'] then
284 | headers['Host'] = host
285 | end
286 | if not headers['User-Agent'] then
287 | headers['User-Agent'] = 'luajit-libuv'
288 | end
289 |
290 | local tcp = uv_tcp_t()
291 | local client = tcp:connect(host, tonumber(port))
292 | client:write(method:upper() .. ' ' .. path .. '?' .. query .. ' HTTP/1.1\r\n')
293 | for header, value in pairs(headers) do
294 | client:write(header .. ': ' .. value .. '\r\n')
295 | end
296 | client:write('Content-Length: ' .. #body .. '\r\n\r\n')
297 | client:write(body)
298 |
299 | local response = parse_http(client, libhttp_parser.HTTP_RESPONSE)
300 |
301 | client:close()
302 |
303 | return response
304 | end
305 |
306 | function http.format_date(time)
307 | return os.date("!%a, %d %b %Y %H:%M:%S GMT", tonumber(time))
308 | end
309 |
310 | local month_atoi = {
311 | Jan = 1,
312 | Feb = 2,
313 | Mar = 3,
314 | Apr = 4,
315 | May = 5,
316 | Jun = 6,
317 | Jul = 7,
318 | Aug = 8,
319 | Sep = 9,
320 | Oct = 10,
321 | Nov = 11,
322 | Dec = 12,
323 | }
324 |
325 | local function get_timezone_offset(ts)
326 | local utcdate = os.date("!*t", ts)
327 | local localdate = os.date("*t", ts)
328 | localdate.isdst = false -- this is the trick
329 | return os.difftime(os.time(localdate), os.time(utcdate))
330 | end
331 |
332 | function http.parse_date(s)
333 | local rfc1123 = '%w+, (%d+) (%w+) (%d+) (%d+):(%d+):(%d+) GMT' -- Sun, 06 Nov 1994 08:49:37 GMT
334 | local rfc1036 = '%w+, (%d+)-(%w+)-(%d+) (%d+):(%d+):(%d+) GMT' -- Sunday, 06-Nov-94 08:49:37 GMT
335 | local asctime = '%w+ (%w+) +(%d+) (%d+):(%d+):(%d+) (%d+)' -- Sun Nov 6 08:49:37 1994
336 |
337 | local day, month, year, hour, min, sec
338 |
339 | day, month, year, hour, min, sec = s:match(rfc1123)
340 | if day then
341 | -- print('RFC 1123: ' .. s)
342 | else
343 | day, month, year, hour, min, sec = s:match(rfc1036)
344 | if day then
345 | -- print('RFC 1036: ' .. s)
346 | if tonumber(year) >= 0 then
347 | year = '19' .. year
348 | else
349 | year = '20' .. year
350 | end
351 | else
352 | month, day, hour, min, sec, year = s:match(asctime)
353 | if day then
354 | -- print('asctime: ' .. s)
355 | else
356 | return
357 | end
358 | end
359 | end
360 |
361 | local time = os.time {
362 | year = tonumber(year),
363 | month = assert(month_atoi[month], 'invalid month'),
364 | day = tonumber(day),
365 | hour = tonumber(hour),
366 | min = tonumber(min),
367 | sec = tonumber(sec),
368 | isdst = false,
369 | }
370 | return time + get_timezone_offset(time)
371 | end
372 |
373 | return http
374 |
--------------------------------------------------------------------------------
/src/uv/init.lua:
--------------------------------------------------------------------------------
1 | local uv = {}
2 |
3 | uv.fs = require 'uv.fs'
4 | uv.http = require 'uv.http'
5 | uv.loop = require 'uv.loop'
6 | uv.parallel = require 'uv.parallel'
7 | uv.process = require 'uv.process'
8 | uv.system = require 'uv.system'
9 | uv.timer = require 'uv.timer'
10 | uv.url = require 'uv.url'
11 |
12 | return uv
13 |
--------------------------------------------------------------------------------
/src/uv/lib/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pguillory/luajit-libuv/dc0ec6e9de02011783d01ff99788be27b444c53e/src/uv/lib/.gitignore
--------------------------------------------------------------------------------
/src/uv/libc.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 |
3 | ffi.cdef [[
4 | void *malloc(size_t size);
5 | void free(void *ptr);
6 | uint16_t ntohs(uint16_t netshort);
7 | mode_t umask(mode_t mask);
8 | uid_t getuid(void);
9 | gid_t getgid(void);
10 | pid_t getpid(void);
11 | ]]
12 |
13 | return ffi.C
14 |
--------------------------------------------------------------------------------
/src/uv/libhttp_parser.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 |
3 | local dir = debug.getinfo(1).source:match('@(.*/)') or '.'
4 |
5 | do
6 | local file = io.open(dir .. 'lib/libhttp_parser.min.h')
7 | if not file then
8 | error('libhttp_parser.min.h not found')
9 | end
10 | local header = file:read('*a')
11 | ffi.cdef(header:match('typedef struct http_parser.*'))
12 | file:close()
13 | end
14 |
15 | local ok, lib = pcall(function() return ffi.load(dir .. 'lib/libhttp_parser.dylib') end)
16 | if ok then return lib end
17 |
18 | local ok, lib = pcall(function() return ffi.load(dir .. 'lib/libhttp_parser.so') end)
19 | if ok then return lib end
20 |
21 | assert('libhttp_parser not found')
22 |
--------------------------------------------------------------------------------
/src/uv/libuv.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 |
3 | local dir = debug.getinfo(1).source:match('@(.*/)') or '.'
4 |
5 | do
6 | local file = io.open(dir .. '/lib/libuv.min.h')
7 | if not file then
8 | error('libuv.min.h not found')
9 | end
10 | local header = file:read('*a')
11 | ffi.cdef(header)
12 | file:close()
13 | end
14 |
15 | local ok, lib = pcall(function() return ffi.load(dir .. 'lib/libuv.dylib') end)
16 | if ok and lib then return lib end
17 |
18 | local ok, lib = pcall(function() return ffi.load(dir .. 'lib/libuv.so') end)
19 | if ok and lib then return lib end
20 |
21 | error('libuv not found')
22 |
23 | -- return require 'uv/libuv2'
24 |
--------------------------------------------------------------------------------
/src/uv/libuv2.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "../../deps/libuv/include/uv.h"
3 | #include "libuv2.h"
4 |
5 | void uv2_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
6 | buf->base = malloc(suggested_size);
7 | buf->len = suggested_size;
8 | }
9 |
10 | void uv2_stream_close(uv_stream_t* stream, uv_close_cb close_cb) {
11 | return uv_close((uv_handle_t*) stream, close_cb);
12 | }
13 |
14 | void uv2_tcp_close(uv_tcp_t* tcp, uv_close_cb close_cb) {
15 | return uv_close((uv_handle_t*) tcp, close_cb);
16 | }
17 |
18 | int uv2_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
19 | return uv_accept((uv_stream_t*) server, (uv_stream_t*) client);
20 | }
21 |
22 | int uv2_tcp_read_start(uv_tcp_t* tcp, uv_alloc_cb alloc_cb, uv_read_cb read_cb) {
23 | return uv_read_start((uv_stream_t*) tcp, alloc_cb, read_cb);
24 | }
25 |
26 | int uv2_tcp_read_stop(uv_tcp_t* tcp) {
27 | return uv_read_stop((uv_stream_t*) tcp);
28 | }
29 |
30 | int uv2_tcp_write(uv_write_t* req, uv_tcp_t* tcp, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb) {
31 | return uv_write(req, (uv_stream_t*) tcp, bufs, nbufs, cb);
32 | }
33 |
34 | int uv2_tcp_listen(uv_tcp_t* stream, int backlog, uv_connection_cb cb) {
35 | return uv_listen((uv_stream_t*) stream, backlog, cb);
36 | }
37 |
38 | int uv2_cwd(uv_buf_t* buf) {
39 | return uv_cwd(buf->base, &buf->len);
40 | }
41 |
42 | int uv2_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) {
43 | int flags2 = 0;
44 |
45 | if (flags & 0x0000) flags2 += O_RDONLY;
46 | if (flags & 0x0001) flags2 += O_WRONLY;
47 | if (flags & 0x0002) flags2 += O_RDWR;
48 | //if (flags & 0x0004) flags2 += O_NONBLOCK;
49 | if (flags & 0x0008) flags2 += O_APPEND;
50 | //if (flags & 0x0010) flags2 += O_SHLOCK;
51 | //if (flags & 0x0020) flags2 += O_EXLOCK;
52 | //if (flags & 0x0040) flags2 += O_ASYNC;
53 | //if (flags & 0x0100) flags2 += O_NOFOLLOW;
54 | if (flags & 0x0200) flags2 += O_CREAT;
55 | if (flags & 0x0400) flags2 += O_TRUNC;
56 | //if (flags & 0x0800) flags2 += O_EXCL;
57 |
58 | return uv_fs_open(loop, req, path, flags2, mode, cb);
59 | }
60 |
61 | int uv2_exepath(uv_buf_t* buffer) {
62 | return uv_exepath(buffer->base, &buffer->len);
63 | }
64 |
65 | int uv2_sigkill() {
66 | return SIGKILL;
67 | }
68 |
69 | int uv2_sighup() {
70 | return SIGHUP;
71 | }
72 |
73 | int uv2_sigint() {
74 | return SIGINT;
75 | }
76 |
77 | int uv2_sigwinch() {
78 | return SIGWINCH;
79 | }
80 |
--------------------------------------------------------------------------------
/src/uv/libuv2.h:
--------------------------------------------------------------------------------
1 |
2 | void uv2_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
3 | void uv2_stream_close(uv_stream_t* stream, uv_close_cb close_cb);
4 | void uv2_tcp_close(uv_tcp_t* stream, uv_close_cb close_cb);
5 | int uv2_tcp_accept(uv_tcp_t* server, uv_tcp_t* client);
6 | int uv2_tcp_read_start(uv_tcp_t*, uv_alloc_cb alloc_cb, uv_read_cb read_cb);
7 | int uv2_tcp_read_stop(uv_tcp_t*);
8 | int uv2_tcp_write(uv_write_t* req, uv_tcp_t* tcp, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
9 | int uv2_tcp_listen(uv_tcp_t* stream, int backlog, uv_connection_cb cb);
10 | int uv2_cwd(uv_buf_t* buf);
11 | int uv2_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb);
12 | int uv2_exepath(uv_buf_t* buffer);
13 | int uv2_sigkill();
14 | int uv2_sighup();
15 | int uv2_sigint();
16 | int uv2_sigwinch();
17 |
--------------------------------------------------------------------------------
/src/uv/libuv2.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 |
3 | local dir = debug.getinfo(1).source:match('@(.*/)') or '.'
4 |
5 | do
6 | local file = io.open(dir .. 'libuv2.h')
7 | if not file then
8 | error('libuv2.h not found')
9 | end
10 | local header = file:read('*a')
11 | ffi.cdef(header)
12 | file:close()
13 | end
14 |
15 | local ok, lib = pcall(function() return ffi.load(dir .. 'lib/libuv2.dylib') end)
16 | if ok and lib then return lib end
17 |
18 | local ok, lib = pcall(function() return ffi.load(dir .. 'lib/libuv2.so') end)
19 | if ok and lib then return lib end
20 |
21 | error('libuv2 not found')
22 |
--------------------------------------------------------------------------------
/src/uv/loop.lua:
--------------------------------------------------------------------------------
1 | require 'uv/ctypes/init'
2 | local ffi = require 'ffi'
3 | local timer = require 'uv.timer'
4 | local libuv = require 'uv/libuv'
5 | local libuv2 = require 'uv/libuv2'
6 | local uv_idle_t = require 'uv/ctypes/uv_idle_t'
7 | local uv_prepare_t = require 'uv/ctypes/uv_prepare_t'
8 | local uv_check_t = require 'uv/ctypes/uv_check_t'
9 | local join = require 'uv/util/join'
10 |
11 | local loop = {}
12 |
13 | function loop.run(callback)
14 | if callback then
15 | timer.set(0, callback)
16 | end
17 | return libuv.uv_default_loop():run()
18 | end
19 |
20 | function loop.alive()
21 | return libuv.uv_loop_alive(libuv.uv_default_loop()) ~= 0
22 | end
23 |
24 | function loop.stop()
25 | libuv.uv_default_loop():stop()
26 | end
27 |
28 | function loop.idle(callback)
29 | join(coroutine.create(function()
30 | uv_idle_t():start(callback)
31 | end))
32 | end
33 |
34 | function loop.yield(callback)
35 | join(coroutine.create(function()
36 | uv_prepare_t():start(callback)
37 | end))
38 | end
39 |
40 | function loop.resume(callback)
41 | join(coroutine.create(function()
42 | uv_check_t():start(callback)
43 | end))
44 | end
45 |
46 | return loop
47 |
--------------------------------------------------------------------------------
/src/uv/parallel.lua:
--------------------------------------------------------------------------------
1 | require 'uv/ctypes/init'
2 | local join = require 'uv/util/join'
3 | local timer = require 'uv.timer'
4 | local libuv = require 'uv/libuv'
5 |
6 | local parallel = {}
7 |
8 | function parallel.map(inputs, callback)
9 | local outputs = {}
10 |
11 | parallel.range(#inputs, function(i)
12 | outputs[i] = callback(inputs[i])
13 | end)
14 |
15 | return outputs
16 | end
17 |
18 | function parallel.range(n, callback)
19 | local thread = coroutine.running()
20 | local busy = n
21 |
22 | for i = 1, n do
23 | timer.set(0, function()
24 | callback(i)
25 | busy = busy - 1
26 | if busy == 0 then
27 | join(thread)
28 | end
29 | end)
30 | end
31 |
32 | if thread then
33 | coroutine.yield()
34 | else
35 | busy = 0
36 | libuv.uv_default_loop():run()
37 | end
38 | end
39 |
40 | return parallel
41 |
--------------------------------------------------------------------------------
/src/uv/process.lua:
--------------------------------------------------------------------------------
1 | require 'uv/ctypes/init'
2 | local ffi = require 'ffi'
3 | local async = require 'uv/util/async'
4 | local ctype = require 'uv/util/ctype'
5 | local libuv = require 'uv/libuv'
6 | local libuv2 = require 'uv/libuv2'
7 | local libc = require 'uv/libc'
8 | local uv_buf_t = require 'uv/ctypes/uv_buf_t'
9 | local uv_signal_t = require 'uv/ctypes/uv_signal_t'
10 | local uv_process_t = require 'uv/ctypes/uv_process_t'
11 | local uv_process_options_t = require 'uv/ctypes/uv_process_options_t'
12 | local join = require 'uv/util/join'
13 | local verify = require 'uv/util/verify'
14 |
15 | local signals = {
16 | kill = libuv2.uv2_sigkill(),
17 | int = libuv2.uv2_sigint(),
18 | hup = libuv2.uv2_sighup(),
19 | winch = libuv2.uv2_sigwinch(),
20 | }
21 |
22 | local signum_atoi = {}
23 | for k, v in pairs(signals) do
24 | signum_atoi[k] = v
25 | signum_atoi['SIG' .. k:upper()] = v
26 | signum_atoi[v] = v
27 | end
28 |
29 | local process = {}
30 |
31 | function process.pid()
32 | return libc.getpid();
33 | end
34 |
35 | function process.on(signum, callback)
36 | local signum = signum_atoi[signum]
37 | local sig = uv_signal_t()
38 | join(coroutine.create(function()
39 | sig:start(signum, callback)
40 | end))
41 | return sig
42 | end
43 |
44 | function process.kill(pid, signum)
45 | local signum = signum_atoi[signum or 'kill']
46 | verify(libuv.uv_kill(pid, signum))
47 | end
48 |
49 | function process.path()
50 | local buf = uv_buf_t()
51 | local status = libuv2.uv2_exepath(buf)
52 | assert(status == 0)
53 | local result = ffi.string(buf.base, buf.len)
54 | buf:free()
55 | return result
56 | end
57 |
58 | function process.usage()
59 | local usage = ffi.new('uv_rusage_t')
60 | verify(libuv.uv_getrusage(usage))
61 | local result = {
62 | utime = usage.ru_utime.tv_usec,
63 | stime = usage.ru_stime.tv_usec,
64 | maxrss = usage.ru_maxrss,
65 | ixrss = usage.ru_ixrss,
66 | idrss = usage.ru_idrss,
67 | isrss = usage.ru_isrss,
68 | minflt = usage.ru_minflt,
69 | majflt = usage.ru_majflt,
70 | nswap = usage.ru_nswap,
71 | inblock = usage.ru_inblock,
72 | oublock = usage.ru_oublock,
73 | msgsnd = usage.ru_msgsnd,
74 | msgrcv = usage.ru_msgrcv,
75 | nsignals = usage.ru_nsignals,
76 | nvcsw = usage.ru_nvcsw,
77 | nivcsw = usage.ru_nivcsw,
78 | }
79 | return result
80 | end
81 |
82 | function process.title(value)
83 | local buf = uv_buf_t()
84 | verify(libuv.uv_get_process_title(buf.base, buf.len))
85 | local title = ffi.string(buf.base)
86 | buf:free()
87 | if value then
88 | verify(uv_set_process_title(value))
89 | end
90 | return title
91 | end
92 |
93 | function process.spawn(args)
94 | local options = uv_process_options_t()
95 |
96 | options.exit_cb = async.uv_exit_cb
97 |
98 | assert(#args >= 1, 'path to executable required')
99 | options.file = args[1]
100 |
101 | options.args = ffi.new('char*[?]', #args + 1)
102 | for i, arg in ipairs(args) do
103 | options.args[i - 1] = ffi.cast('char*', arg)
104 | end
105 |
106 | options.stdio_count = 3
107 | local stdio = ffi.new('uv_stdio_container_t[?]', 3)
108 | options.stdio = stdio
109 |
110 | if type(args.stdin) == 'number' then
111 | options.stdio[0].flags = libuv.UV_INHERIT_FD
112 | options.stdio[0].data.fd = args.stdin
113 | end
114 |
115 | if type(args.stdout) == 'number' then
116 | options.stdio[1].flags = libuv.UV_INHERIT_FD
117 | options.stdio[1].data.fd = args.stdout
118 | end
119 |
120 | if type(args.stderr) == 'number' then
121 | options.stdio[2].flags = libuv.UV_INHERIT_FD
122 | options.stdio[2].data.fd = args.stderr
123 | end
124 |
125 | if args.uid then
126 | options.uid = args.uid
127 | options.flags = bit.bor(options.flags, libuv.UV_PROCESS_SETUID)
128 | end
129 |
130 | if args.gid then
131 | options.gid = args.gid
132 | options.flags = bit.bor(options.flags, libuv.UV_PROCESS_SETGID)
133 | end
134 |
135 | local req = uv_process_t()
136 | local term_signal = req:spawn(options)
137 | options:free()
138 | return term_signal
139 | end
140 |
141 | return process
142 |
--------------------------------------------------------------------------------
/src/uv/system.lua:
--------------------------------------------------------------------------------
1 | require 'uv/ctypes/init'
2 | local ffi = require 'ffi'
3 | local libuv = require 'uv/libuv'
4 | local libuv2 = require 'uv/libuv2'
5 | local verify = require 'uv/util/verify'
6 |
7 | local system = {}
8 |
9 | function system.free_memory()
10 | return libuv.uv_get_free_memory()
11 | end
12 |
13 | function system.total_memory()
14 | return libuv.uv_get_total_memory()
15 | end
16 |
17 | function system.hrtime()
18 | return tonumber(libuv.uv_hrtime()) / 1000000000
19 | end
20 |
21 | function system.loadavg()
22 | local avg = ffi.new('double[?]', 3)
23 | libuv.uv_loadavg(avg)
24 | return avg[0], avg[1], avg[2]
25 | end
26 |
27 | function system.uptime()
28 | local time = ffi.new('double[1]')
29 | verify(libuv.uv_uptime(time))
30 | return time[0]
31 | end
32 |
33 | return system
34 |
--------------------------------------------------------------------------------
/src/uv/tcp.lua:
--------------------------------------------------------------------------------
1 | require 'uv/ctypes/init'
2 | local class = require 'uv/util/class'
3 | local ffi = require 'ffi'
4 | local libuv = require 'uv/libuv'
5 | local libuv2 = require 'uv/libuv2'
6 | local uv_tcp_t = require 'uv/ctypes/uv_tcp_t'
7 | local join = require 'uv/util/join'
8 |
9 | local tcp = {}
10 |
11 | function tcp.listen(host, port, callback)
12 | local server = uv_tcp_t()
13 | server:bind(host, port)
14 | server:listen()
15 | return server
16 | end
17 |
18 | function tcp.connect(host, port, callback)
19 | local client = uv_tcp_t()
20 | return client:connect(host, tonumber(port))
21 | end
22 |
23 | return tcp
24 |
--------------------------------------------------------------------------------
/src/uv/timer.lua:
--------------------------------------------------------------------------------
1 | require 'uv/ctypes/init'
2 | local join = require 'uv/util/join'
3 | local uv_timer_t = require 'uv/ctypes/uv_timer_t'
4 |
5 | local timer = {}
6 |
7 | function timer.set(timeout, callback)
8 | join(coroutine.create(function()
9 | local timer = uv_timer_t()
10 | timer:sleep(timeout)
11 | timer:free()
12 | callback()
13 | end))
14 | end
15 |
16 | function timer.every(timeout, callback)
17 | join(coroutine.create(function()
18 | local timer = uv_timer_t()
19 | timer:every(timeout, callback)
20 | timer:free()
21 | end))
22 | end
23 |
24 | function timer.sleep(timeout)
25 | local timer = uv_timer_t()
26 | timer:sleep(timeout)
27 | timer:free()
28 | end
29 |
30 | return timer
31 |
--------------------------------------------------------------------------------
/src/uv/url.lua:
--------------------------------------------------------------------------------
1 | require 'uv/ctypes/init'
2 | local class = require 'uv/util/class'
3 | local ffi = require 'ffi'
4 | local libhttp_parser = require 'uv/libhttp_parser'
5 |
6 | local url = {}
7 |
8 | function url.split(s, parts)
9 | if s:sub(1, 2) == '//' then
10 | local parts = url.split('http:' .. s, parts)
11 | parts.schema = nil
12 | return parts
13 | end
14 |
15 | parts = parts or {}
16 |
17 | local struct = ffi.new('struct http_parser_url')
18 | local status = libhttp_parser.http_parser_parse_url(s, #s, 0, struct)
19 | if status ~= 0 then
20 | if s:sub(1, 1) ~= '/' then
21 | local parts = url.split('/' .. s, parts)
22 | parts.path = parts.path:sub(2)
23 | return parts
24 | end
25 | error('error parsing url')
26 | end
27 |
28 | local function segment(name, id)
29 | if bit.band(struct.field_set, bit.lshift(1, id)) > 0 then
30 | local field = struct.field_data[id]
31 | parts[name] = s:sub(field.off + 1, field.off + field.len)
32 | end
33 | end
34 |
35 | segment('schema', libhttp_parser.UF_SCHEMA)
36 | segment('host', libhttp_parser.UF_HOST)
37 | segment('port', libhttp_parser.UF_PORT)
38 | segment('path', libhttp_parser.UF_PATH)
39 | segment('query', libhttp_parser.UF_QUERY)
40 | segment('fragment', libhttp_parser.UF_FRAGMENT)
41 | segment('userinfo', libhttp_parser.UF_USERINFO)
42 |
43 | return parts
44 | end
45 |
46 | function url.join(parts)
47 | return (parts.schema and (parts.schema .. '://') or (parts.host and '//' or ''))
48 | .. (parts.userinfo and (parts.userinfo .. '@') or '')
49 | .. (parts.host or '')
50 | .. (parts.port and (':' .. parts.port) or '')
51 | .. (parts.path or '')
52 | .. (parts.query and ('?' .. parts.query) or '')
53 | .. (parts.fragment and ('#' .. parts.fragment) or '')
54 | end
55 |
56 | function url.encode(value)
57 | if type(value) == 'table' then
58 | local names = {}
59 | for name in pairs(value) do
60 | table.insert(names, name)
61 | end
62 | table.sort(names)
63 |
64 | local buffer = {}
65 | for _, name in ipairs(names) do
66 | table.insert(buffer, url.encode(name) .. '=' .. url.encode(value[name]))
67 | end
68 | return table.concat(buffer, '&')
69 | elseif type(value) == 'string' then
70 | return (value:gsub('[^%w]', function(c)
71 | return string.format('%%%02X', string.byte(c))
72 | end))
73 | elseif type(value) == 'nil' then
74 | return ''
75 | else
76 | return url.encode(tostring(value))
77 | end
78 | end
79 |
80 | function url.relative(base, relative)
81 | local base = url.split(base)
82 | local relative = url.split(relative)
83 |
84 | local result = {
85 | schema = relative.schema or base.schema,
86 | userinfo = relative.userinfo or base.userinfo,
87 | host = relative.host or base.host,
88 | port = relative.port or base.port,
89 | path = relative.path or base.path,
90 | query = relative.query or base.query,
91 | fragment = relative.fragment or base.fragment,
92 | }
93 |
94 | if relative.path == '' or relative.path == nil then
95 | result.path = base.path
96 | elseif relative.path:match('^/') then
97 | result.path = relative.path
98 | else
99 | result.path = base.path:gsub('[^/]*$', relative.path, 1)
100 | end
101 |
102 | return url.join(result)
103 | end
104 |
105 | return url
106 |
--------------------------------------------------------------------------------
/src/uv/util/async.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local join = require 'uv/util/join'
3 | local libuv = require 'uv/libuv'
4 |
5 | local async, threads, id = {}, {}, 0
6 | setmetatable(async, async)
7 |
8 | local red_on_black = '\27[31;40m'
9 | local green_on_black = '\27[32;40m'
10 | local yellow_on_black = '\27[33;40m'
11 | local white_on_black = '\27[37;40m'
12 |
13 | local function log(color, method, id, req, info)
14 | local source = info.short_src .. ':' .. info.currentline
15 | print(string.format('%s%-8s %-5i %-42s %-10s %s%s', color, method, id, req, info.name, source, white_on_black))
16 | io.flush()
17 | end
18 |
19 | function async.yield(req)
20 | local thread = coroutine.running()
21 |
22 | if not thread then
23 | local retval
24 | local thread = coroutine.create(function()
25 | retval = { async.yield(req) }
26 | end)
27 | local ok = coroutine.resume(thread)
28 | libuv.uv_default_loop():run()
29 | return unpack(retval)
30 | end
31 |
32 | while threads[id] do
33 | id = bit.band(id + 1, 0xFFFFFFFFFFFF) -- lower 48 bits
34 | end
35 | -- log(yellow_on_black, 'yield', id, req, debug.getinfo(2))
36 | req.data = ffi.cast('void*', id)
37 | threads[id] = thread
38 | return coroutine.yield()
39 | end
40 |
41 | function async.resume(req, ...)
42 | local id = tonumber(ffi.cast('int', req.data))
43 | local thread = threads[id]
44 | -- log(green_on_black, 'resume', id, req, debug.getinfo(thread, 1))
45 | threads[id] = nil
46 | return join(thread, ...)
47 | end
48 |
49 | function async:__index(ctype)
50 | self[ctype] = ffi.cast(ctype, self.resume)
51 | return self[ctype]
52 | end
53 |
54 | return async
55 |
--------------------------------------------------------------------------------
/src/uv/util/class.lua:
--------------------------------------------------------------------------------
1 | local function class(constructor, parent)
2 | local class, mt = {}, {}
3 | class.__index = class
4 | if constructor then
5 | function mt:__call(...)
6 | return setmetatable(constructor(...), self)
7 | end
8 | else
9 | function mt:__call()
10 | return setmetatable({}, self)
11 | end
12 | end
13 | if parent then
14 | mt.__index = parent
15 | end
16 | return setmetatable(class, mt)
17 | end
18 |
19 | return class
20 |
--------------------------------------------------------------------------------
/src/uv/util/ctype.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 |
3 | local function ctype(name, constructor, destructor)
4 | local metatype = {}
5 | local metatable = {}
6 | metatype.__index = metatype
7 | local ctype = ffi.metatype(name, metatype)
8 | local sizeof = ffi.sizeof(ctype)
9 | assert(sizeof > 0)
10 | if constructor then
11 | function metatable:__call(...)
12 | return constructor(...)
13 | end
14 | else
15 | function metatable:__call(...)
16 | return ctype(...)
17 | end
18 | end
19 | return setmetatable(metatype, metatable)
20 | end
21 |
22 | return ctype
23 |
--------------------------------------------------------------------------------
/src/uv/util/errno.lua:
--------------------------------------------------------------------------------
1 | local ffi = require 'ffi'
2 | local libuv = require 'uv/libuv'
3 |
4 | local errno_mt = {}
5 |
6 | local errno = setmetatable({}, {
7 | __call = function(self, n)
8 | return ffi.string(libuv.uv_err_name(n)) .. ': ' .. ffi.string(libuv.uv_strerror(n))
9 | end,
10 | __index = function(self, n)
11 | self[n] = self(n)
12 | return self[n]
13 | end,
14 | })
15 |
16 | assert(errno[-1] == 'EPERM: operation not permitted')
17 | assert(errno[-2] == 'ENOENT: no such file or directory')
18 |
19 | return errno
20 |
--------------------------------------------------------------------------------
/src/uv/util/expect.lua:
--------------------------------------------------------------------------------
1 |
2 | local expect = {}
3 |
4 | local tosource = function(value)
5 | if type(value) == 'string' then
6 | return string.format('%q', value)
7 | else
8 | return tostring(value)
9 | end
10 | end
11 |
12 | function expect.equal(a, b)
13 | if a ~= b then
14 | local err = string.format('values should be equal:\n- %s\n- %s', tosource(a), tosource(b))
15 | -- print(err)
16 | error(err, 2)
17 | end
18 | end
19 |
20 | function expect.error(expected, callback)
21 | local ok, actual = pcall(callback)
22 | if ok then
23 | local err = string.format('expected an error but got none:\n- %q', expected)
24 | -- print(err)
25 | error(err, 2)
26 | end
27 | if not actual:find(expected, 1, true) then
28 | local err = string.format('expected a different error:\n- (expect): %q\n- (actual): %q', expected, actual)
29 | -- print(err)
30 | error(err, 2)
31 | end
32 | end
33 |
34 | function expect.ok(callback)
35 | local ok, actual = pcall(callback)
36 | if not ok then
37 | local err = string.format('expected no errors but got one:\n- %q', actual)
38 | -- print(err)
39 | error(err, 2)
40 | end
41 | end
42 |
43 | do
44 | expect.error('values should be equal', function()
45 | expect.equal('asdf', 'zxcv')
46 | end)
47 |
48 | expect.error('expected an error but got none', function()
49 | expect.error('something', function()
50 | end)
51 | end)
52 |
53 | expect.error('expected a different error', function()
54 | expect.error('something', function()
55 | error('anything else')
56 | end)
57 | end)
58 |
59 | expect.ok(function()
60 | expect.ok(function()
61 | end)
62 | end)
63 |
64 | expect.error('expected no errors but got one', function()
65 | expect.ok(function()
66 | error('something')
67 | end)
68 | end)
69 | end
70 |
71 | return expect
72 |
--------------------------------------------------------------------------------
/src/uv/util/join.lua:
--------------------------------------------------------------------------------
1 | local function join(thread, ...)
2 | local ok, err = coroutine.resume(thread, ...)
3 | if not ok then
4 | return error(debug.traceback(thread, err), 0)
5 | end
6 | end
7 |
8 | return join
9 |
--------------------------------------------------------------------------------
/src/uv/util/strict.lua:
--------------------------------------------------------------------------------
1 | --
2 | -- strict.lua
3 | -- checks uses of undeclared global variables
4 | -- All global variables must be 'declared' through a regular assignment
5 | -- (even assigning nil will do) in a main chunk before being used
6 | -- anywhere or assigned to inside a function.
7 | --
8 |
9 | local mt = getmetatable(_G)
10 | if mt == nil then
11 | mt = {}
12 | setmetatable(_G, mt)
13 | end
14 |
15 | __STRICT = true
16 | mt.__declared = {}
17 |
18 | mt.__newindex = function (t, n, v)
19 | if __STRICT and not mt.__declared[n] then
20 | local w = debug.getinfo(2, "S").what
21 | if w ~= "main" and w ~= "C" then
22 | error("assign to undeclared variable '"..n.."'", 2)
23 | end
24 | mt.__declared[n] = true
25 | end
26 | rawset(t, n, v)
27 | end
28 |
29 | mt.__index = function (t, n)
30 | if not mt.__declared[n] and debug.getinfo(2, "S").what ~= "C" then
31 | error("variable '"..n.."' is not declared", 2)
32 | end
33 | return rawget(t, n)
34 | end
35 |
36 | function global(...)
37 | for _, v in ipairs{...} do mt.__declared[v] = true end
38 | end
39 |
--------------------------------------------------------------------------------
/src/uv/util/verify.lua:
--------------------------------------------------------------------------------
1 | local errno = require 'uv/util/errno'
2 |
3 | local function verify(status)
4 | if tonumber(status) < 0 then
5 | error(errno[status], 2)
6 | end
7 | return tonumber(status)
8 | end
9 |
10 | return verify
11 |
--------------------------------------------------------------------------------
/test/fs_test.lua:
--------------------------------------------------------------------------------
1 | require 'uv/util/strict'
2 | local loop = require 'uv.loop'
3 | local fs = require 'uv.fs'
4 | local libc = require 'uv/libc'
5 | local expect = require 'uv/util/expect'
6 |
7 | local ffi = require 'ffi'
8 | local uid = libc.getuid()
9 | local gid = libc.getgid()
10 |
11 | do
12 | fs.with_tempdir(function(dir)
13 | expect.equal(fs.cwd():find(dir, 1, true), nil)
14 | fs.chdir(dir)
15 | assert(fs.cwd():find(dir, 1, true))
16 |
17 | -- writing
18 | local file = fs.open('file.txt', 'w', '777')
19 | file:write('hello!')
20 | file:close()
21 |
22 | -- reading
23 | local file = fs.open('file.txt')
24 | expect.equal(file:read(), 'hello!')
25 | file:sync()
26 | file:close()
27 |
28 | fs.writefile('file2.txt', 'hello!')
29 | expect.equal(fs.readfile('file2.txt'), 'hello!')
30 | fs.unlink('file2.txt')
31 |
32 | -- hard links
33 | fs.link('file.txt', 'link.txt')
34 | local file = fs.open('link.txt')
35 | expect.equal(file:read(), 'hello!')
36 | file:close()
37 | fs.unlink('link.txt')
38 |
39 | -- symlinks
40 | fs.symlink('file.txt', 'symlink.txt')
41 | expect.equal(fs.readlink('symlink.txt'), 'file.txt')
42 | fs.unlink('symlink.txt')
43 |
44 | local stat = fs.stat('file.txt')
45 | expect.equal(stat.uid, uid)
46 | -- This doesn't work! stat.gid is returning 0 for some reason.
47 | -- expect.equal(stat.gid, gid)
48 | expect.equal(stat.mode, 511) -- octal('777')
49 | expect.equal(stat.size, 6)
50 | expect.equal(stat.is_dir, false)
51 | expect.equal(stat.is_fifo, false)
52 | assert(math.abs(os.time() - tonumber(stat.atime)) < 10)
53 |
54 | -- renaming
55 | fs.rename('file.txt', 'new-file.txt')
56 | local file = fs.open('new-file.txt')
57 | expect.equal(file:read(), 'hello!')
58 | file:close()
59 | fs.unlink('new-file.txt')
60 |
61 | fs.open('a', 'w'):close()
62 | fs.open('b', 'w'):close()
63 | fs.mkdir('c')
64 | fs.open('c/d', 'w'):close()
65 | fs.open('c/e', 'w'):close()
66 | fs.mkdir('c/f')
67 | fs.open('c/f/g', 'w'):close()
68 | fs.open('c/f/h', 'w'):close()
69 |
70 | local filenames = fs.readdir('.')
71 | expect.equal(#filenames, 3)
72 | expect.equal(filenames[1], 'a')
73 | expect.equal(filenames[2], 'b')
74 | expect.equal(filenames[3], 'c')
75 |
76 | local filenames = fs.readdir_r('.')
77 | expect.equal(#filenames, 6)
78 | expect.equal(filenames[1], 'a')
79 | expect.equal(filenames[2], 'b')
80 | expect.equal(filenames[3], 'c/d')
81 | expect.equal(filenames[4], 'c/e')
82 | expect.equal(filenames[5], 'c/f/g')
83 | expect.equal(filenames[6], 'c/f/h')
84 |
85 | local filenames = fs.readdir_r('c')
86 | expect.equal(#filenames, 4)
87 | expect.equal(filenames[1], 'd')
88 | expect.equal(filenames[2], 'e')
89 | expect.equal(filenames[3], 'f/g')
90 | expect.equal(filenames[4], 'f/h')
91 |
92 | -- errors
93 | local ok, err = pcall(function()
94 | fs.open('nonexistent')
95 | end)
96 | assert(not ok)
97 | assert(err:find('ENOENT: no such file or directory'), err)
98 |
99 | local ok, err = pcall(function()
100 | fs.chdir('nonexistent')
101 | end)
102 | assert(not ok)
103 | assert(err:find('ENOENT: no such file or directory'), err)
104 | end)
105 | end
106 |
--------------------------------------------------------------------------------
/test/http_test.lua:
--------------------------------------------------------------------------------
1 | require 'uv/util/strict'
2 | local loop = require 'uv.loop'
3 | local http = require 'uv.http'
4 | local join = require 'uv/util/join'
5 | local expect = require 'uv/util/expect'
6 |
7 | --------------------------------------------------------------------------------
8 | -- basic server
9 | --------------------------------------------------------------------------------
10 |
11 | loop.run(function()
12 | local server = http.listen('127.0.0.1', 7000, function(request)
13 | expect.equal(request.ip, '127.0.0.1')
14 | expect.equal(request.method, 'GET')
15 | expect.equal(request.path, '/path/to/route')
16 | expect.equal(request.query, 'a=1&b=2')
17 | expect.equal(request.headers['User-Agent'], 'test')
18 |
19 | return { status = 200, headers = { Expires = '-1' }, body = 'hello world' }
20 | end)
21 |
22 | local response = http.request {
23 | url = 'http://127.0.0.1:7000/path/to/route?a=1&b=2',
24 | headers = { ['User-Agent'] = 'test' },
25 | }
26 |
27 | expect.equal(response.status, 200)
28 | expect.equal(response.headers['Expires'], '-1')
29 | expect.equal(response.headers['Content-Length'], '11')
30 | expect.equal(response.body, 'hello world')
31 |
32 | local response = http.request{
33 | host = '127.0.0.1', port = 7000, path = '/path/to/route', query = 'a=1&b=2',
34 | headers = { ['User-Agent'] = 'test' },
35 | }
36 |
37 | expect.equal(response.status, 200)
38 | expect.equal(response.headers['Expires'], '-1')
39 | expect.equal(response.headers['Content-Length'], '11')
40 | expect.equal(response.body, 'hello world')
41 |
42 | server:close()
43 | end)
44 |
45 | loop.run(function()
46 | local server = http.listen('127.0.0.1', 7000, function(request)
47 | expect.equal(request.method, 'POST')
48 | return { status = 200 }
49 | end)
50 |
51 | for i = 1, 10 do
52 | local response = http.request {
53 | url = 'http://127.0.0.1:7000/?a=1&b=2',
54 | method = 'post', body = 'b=3&c=4',
55 | }
56 | collectgarbage()
57 | end
58 | collectgarbage()
59 |
60 | server:close()
61 | end)
62 |
63 | -- loop.run(function()
64 | -- local response = http.request{
65 | -- url = 'http://pygments.appspot.com/',
66 | -- method = 'post', body = 'lang=lua&code=print',
67 | -- }
68 | -- end)
69 |
70 | --------------------------------------------------------------------------------
71 | -- manually invoked event loop
72 | --------------------------------------------------------------------------------
73 |
74 | do
75 | local server, response
76 |
77 | join(coroutine.create(function()
78 | server = http.listen('127.0.0.1', 7000, function(request)
79 | return { status = 200, body = 'ok' }
80 | end)
81 | end))
82 |
83 | join(coroutine.create(function()
84 | response = http.request { url = 'http://127.0.0.1:7000/' }
85 | server:close()
86 | end))
87 |
88 | loop.run()
89 |
90 | expect.equal(response.body, 'ok')
91 | end
92 |
93 | --------------------------------------------------------------------------------
94 | -- dates
95 | --------------------------------------------------------------------------------
96 |
97 | do
98 | expect.equal(http.format_date(1408986974LL), 'Mon, 25 Aug 2014 17:16:14 GMT')
99 | -- expect.equal(http.parse_date('Mon, 25 Aug 2014 17:16:14 GMT'), 1408986974LL)
100 |
101 | expect.equal(http.format_date(784111777), 'Sun, 06 Nov 1994 08:49:37 GMT')
102 | expect.equal(http.parse_date('Sun, 06 Nov 1994 08:49:37 GMT'), 784111777)
103 | expect.equal(http.parse_date('Sunday, 06-Nov-94 08:49:37 GMT'), 784111777)
104 | expect.equal(http.parse_date('Sun Nov 6 08:49:37 1994'), 784111777)
105 | expect.equal(http.parse_date('asdf'), nil)
106 | end
107 |
--------------------------------------------------------------------------------
/test/loop_test.lua:
--------------------------------------------------------------------------------
1 | require 'uv/util/strict'
2 | local loop = require 'uv.loop'
3 | local timer = require 'uv.timer'
4 | local expect = require 'uv/util/expect'
5 |
6 | for i = 1, 1000 do
7 | loop.run(function()
8 | end)
9 | end
10 |
11 | assert(loop.alive() == false)
12 | loop.run(function()
13 | assert(loop.alive() == false)
14 | timer.set(0, function()
15 | assert(loop.alive() == false)
16 | end)
17 | assert(loop.alive() == true)
18 | end)
19 | assert(loop.alive() == false)
20 |
21 | do
22 | local buffer = {}
23 | table.insert(buffer, '1')
24 | loop.run(function()
25 | table.insert(buffer, '2')
26 | timer.set(0, function()
27 | table.insert(buffer, '4')
28 | end)
29 | table.insert(buffer, '3')
30 | end)
31 | table.insert(buffer, '5')
32 | expect.equal(table.concat(buffer), '12345')
33 | end
34 |
35 | loop.run(function()
36 | local buffer = {}
37 | loop.yield(function()
38 | table.insert(buffer, 'yield')
39 | end)
40 | loop.resume(function()
41 | table.insert(buffer, 'resume')
42 | end)
43 | table.insert(buffer, 'before')
44 | timer.sleep(1)
45 | table.insert(buffer, 'after')
46 | expect.equal(table.concat(buffer, ' '), 'before yield resume after')
47 | loop.stop()
48 | end)
49 |
50 | loop.run(function()
51 | local buffer = {}
52 | local count = 0
53 | loop.idle(function()
54 | count = count + 1
55 | if not buffer[2] then
56 | buffer[2] = 'idle'
57 | end
58 | end)
59 | table.insert(buffer, 'before')
60 | timer.sleep(2)
61 | table.insert(buffer, 'after')
62 | expect.equal(table.concat(buffer, ' '), 'before idle after')
63 | assert(count > 0)
64 | loop.stop()
65 | end)
66 |
--------------------------------------------------------------------------------
/test/parallel_test.lua:
--------------------------------------------------------------------------------
1 | require 'uv/util/strict'
2 | local loop = require 'uv.loop'
3 | local parallel = require 'uv.parallel'
4 | local timer = require 'uv.timer'
5 |
6 | do
7 | local log = {}
8 | loop.run(function()
9 | parallel.range(3, function(i)
10 | table.insert(log, i)
11 | timer.sleep(1)
12 | table.insert(log, i)
13 | end)
14 | end)
15 | assert(table.concat(log) == '123123')
16 | end
17 |
18 | do
19 | local log = {}
20 | parallel.range(3, function(i)
21 | table.insert(log, i)
22 | timer.sleep(1)
23 | table.insert(log, i)
24 | end)
25 | assert(table.concat(log) == '123123')
26 | end
27 |
28 | do
29 | local inputs = { 'a', 'b', 'c' }
30 | local log = {}
31 | local outputs = {}
32 |
33 | loop.run(function()
34 | outputs = parallel.map(inputs, function(input)
35 | table.insert(log, input)
36 | timer.sleep(1)
37 | local output = string.upper(input)
38 | table.insert(log, output)
39 | return output
40 | end)
41 | assert(table.concat(outputs) == 'ABC')
42 | end)
43 |
44 | assert(table.concat(log) == 'abcABC')
45 | end
46 |
47 | do
48 | local inputs = { 'a', 'b', 'c' }
49 | local log = {}
50 | local outputs = {}
51 |
52 | outputs = parallel.map(inputs, function(input)
53 | table.insert(log, input)
54 | timer.sleep(1)
55 | local output = string.upper(input)
56 | table.insert(log, output)
57 | return output
58 | end)
59 |
60 | assert(table.concat(outputs) == 'ABC')
61 | assert(table.concat(log) == 'abcABC')
62 | end
63 |
--------------------------------------------------------------------------------
/test/process_test.lua:
--------------------------------------------------------------------------------
1 | require 'uv/util/strict'
2 | local loop = require 'uv.loop'
3 | local process = require 'uv.process'
4 | local timer = require 'uv.timer'
5 | local expect = require 'uv/util/expect'
6 |
7 | do
8 | local signal = process.spawn { './luajit', '-v' }
9 | expect.equal(signal, 0)
10 |
11 | local signal = process.spawn { '/bin/echo', 'Hello', 'world' }
12 | expect.equal(signal, 0)
13 | end
14 |
15 | loop.run(function()
16 | local count = 0
17 | process.on('hup', function()
18 | count = count + 1
19 | end)
20 | process.kill(process.pid(), 'hup')
21 | process.kill(process.pid(), 'hup')
22 | process.kill(process.pid(), 'hup')
23 | timer.sleep(1)
24 | expect.equal(count, 3)
25 | loop.stop()
26 | end)
27 |
28 | do
29 | assert(process.path():find('luajit'))
30 | end
31 |
--------------------------------------------------------------------------------
/test/system_test.lua:
--------------------------------------------------------------------------------
1 | require 'uv/util/strict'
2 | local system = require 'uv.system'
3 |
4 | do
5 | local free, total = system.free_memory(), system.total_memory()
6 | assert(free > 0)
7 | assert(total > 0)
8 | assert(free <= total)
9 | end
10 |
11 | do
12 | local hrtime = system.hrtime()
13 | assert(hrtime > 0)
14 | end
15 |
16 | do
17 | local x, y, z = system.loadavg()
18 | assert(type(x) == 'number')
19 | assert(type(y) == 'number')
20 | assert(type(z) == 'number')
21 | end
22 |
23 | do
24 | local uptime = system.uptime()
25 | assert(uptime > 0)
26 | end
27 |
--------------------------------------------------------------------------------
/test/tcp_test.lua:
--------------------------------------------------------------------------------
1 | require 'uv/util/strict'
2 | local loop = require 'uv.loop'
3 | local tcp = require 'uv.tcp'
4 | local join = require 'uv/util/join'
5 | local expect = require 'uv/util/expect'
6 |
7 | loop.run(function()
8 | local server = tcp.listen('127.0.0.1', 7000)
9 |
10 | join(coroutine.create(function()
11 | while true do
12 | local socket = server:accept()
13 | while true do
14 | local data = socket:read()
15 | if data:find('quit') then
16 | break
17 | end
18 | socket:write(data:upper())
19 | end
20 | socket:close()
21 | end
22 | end))
23 |
24 | local socket = tcp.connect('127.0.0.1', 7000)
25 | socket:write('ping')
26 | expect.equal(socket:read(), 'PING')
27 | socket:write('quit')
28 | socket:close()
29 |
30 | server:close()
31 | end)
32 |
33 | -- loop.run(function()
34 | -- local server = uv_tcp_t()
35 | -- server:bind('127.0.0.1', 7000)
36 | -- server:listen(function(stream)
37 | -- expect.equal(stream:read(), 'foo')
38 | -- stream:write('bar')
39 | -- end)
40 | --
41 | -- local client = uv_tcp_t()
42 | -- local stream = client:connect('127.0.0.1', 7000)
43 | -- stream:write('foo')
44 | -- expect.equal(stream:read(), 'bar')
45 | -- stream:close()
46 | --
47 | -- server:close()
48 | -- end)
49 | --
50 | -- loop.run(function()
51 | -- local getaddrinfo = uv_getaddrinfo_t()
52 | --
53 | -- local addrs = getaddrinfo:getaddrinfo('123.123.123.123', 'https')
54 | -- assert(#addrs > 0)
55 | -- for _, addr in ipairs(addrs) do
56 | -- expect.equal(addr:ip(), '123.123.123.123')
57 | -- expect.equal(addr:port(), 443)
58 | -- end
59 | --
60 | -- -- local addrs = getaddrinfo:getaddrinfo('google.com', 'http')
61 | -- -- for _, addr in ipairs(addrs) do
62 | -- -- assert(addr:ip():match('^74%.'))
63 | -- -- assert(addr:port() == 80)
64 | -- -- end
65 | -- end)
66 |
--------------------------------------------------------------------------------
/test/timer_test.lua:
--------------------------------------------------------------------------------
1 | require 'uv/util/strict'
2 | local loop = require 'uv.loop'
3 | local timer = require 'uv.timer'
4 |
5 | do
6 | local s = ''
7 | timer.set(5, function() s = s .. 'e' end)
8 | timer.set(3, function() s = s .. 'c' end)
9 | timer.set(1, function() s = s .. 'a' end)
10 | timer.set(2, function() s = s .. 'b' end)
11 | timer.set(4, function() s = s .. 'd' end)
12 | timer.set(6, function() s = s .. 'f' end)
13 | loop.run()
14 | assert(s == 'abcdef')
15 | end
16 |
17 | local s = ''
18 | loop.run(function()
19 | timer.set(5, function() s = s .. 'e' end)
20 | timer.set(3, function() s = s .. 'c' end)
21 | timer.set(1, function() s = s .. 'a' end)
22 | timer.set(2, function() s = s .. 'b' end)
23 | timer.set(4, function() s = s .. 'd' end)
24 | timer.set(6, function() s = s .. 'f' end)
25 | end)
26 | assert(s == 'abcdef')
27 |
28 | do
29 | local s = ''
30 | timer.every(1, function(self)
31 | s = s .. 'a'
32 | if #s >= 5 then self:stop() end
33 | end)
34 | loop.run()
35 | assert(s == 'aaaaa')
36 | end
37 |
38 | local s = ''
39 | loop.run(function()
40 | timer.every(1, function(self)
41 | s = s .. 'a'
42 | if #s >= 5 then self:stop() end
43 | end)
44 | end)
45 | assert(s == 'aaaaa')
46 |
--------------------------------------------------------------------------------
/test/url_test.lua:
--------------------------------------------------------------------------------
1 | require 'uv/util/strict'
2 | local expect = require 'uv/util/expect'
3 | local url = require 'uv.url'
4 |
5 | do
6 | local function test(input, schema, userinfo, host, port, path, query, fragment)
7 | local parts = url.split(input)
8 | expect.equal(parts.schema, schema)
9 | expect.equal(parts.userinfo, userinfo)
10 | expect.equal(parts.host, host)
11 | expect.equal(parts.port, port)
12 | expect.equal(parts.path, path)
13 | expect.equal(parts.query, query)
14 | expect.equal(parts.fragment, fragment)
15 | expect.equal(url.join(parts), input)
16 | end
17 |
18 | test('http://user:pass@host:1234/path?a=1#section', 'http', 'user:pass', 'host', '1234', '/path', 'a=1', 'section')
19 | test( '//user:pass@host:1234/path?a=1#section', nil, 'user:pass', 'host', '1234', '/path', 'a=1', 'section')
20 | test('http://' .. 'host:1234/path?a=1#section', 'http', nil, 'host', '1234', '/path', 'a=1', 'section')
21 | test('http://user:pass@host'.. '/path?a=1#section', 'http', 'user:pass', 'host', nil, '/path', 'a=1', 'section')
22 | test( '/path?a=1#section', nil, nil, nil, nil, '/path', 'a=1', 'section')
23 | test( 'path?a=1#section', nil, nil, nil, nil, 'path', 'a=1', 'section')
24 | test('http://user:pass@host:1234'.. '?a=1#section', 'http', 'user:pass', 'host', '1234', nil, 'a=1', 'section')
25 | test('http://user:pass@host:1234/path'..'#section', 'http', 'user:pass', 'host', '1234', '/path', nil, 'section')
26 | test('http://user:pass@host:1234/path?a=1', 'http', 'user:pass', 'host', '1234', '/path', 'a=1', nil)
27 | test('http://user:pass@host:1234', 'http', 'user:pass', 'host', '1234', nil, nil, nil)
28 | end
29 |
30 | do
31 | local function test(input, expected)
32 | local actual = url.encode(input)
33 | expect.equal(actual, expected)
34 | end
35 |
36 | test('asdf', 'asdf')
37 | test(' ', '%20')
38 | test(1, '1')
39 | test(nil, '')
40 | test(false, 'false')
41 | test({}, '')
42 | test({ a = 1 }, 'a=1')
43 | test({ ['a b'] = 'c d' }, 'a%20b=c%20d')
44 | end
45 |
46 | do
47 | local function test(base, relative, expected)
48 | local actual = url.relative(base, relative)
49 | expect.equal(actual, expected)
50 | end
51 |
52 | test('/a/b/c', '/d/e/f', '/d/e/f')
53 | test('/a/b/c', 'd/e/f', '/a/b/d/e/f')
54 | test('/a/b/', 'd/e/f', '/a/b/d/e/f')
55 | test('/a/b/c', '', '/a/b/c')
56 | test('/a/b/c?x=1', '', '/a/b/c?x=1')
57 | test('/a/b/c', '?y=2', '/a/b/c?y=2')
58 | test('/a/b/c?x=1', '?y=2', '/a/b/c?y=2')
59 | end
60 |
--------------------------------------------------------------------------------