├── .gitignore ├── LICENSE ├── README.md ├── benchmark ├── README.md ├── conf │ └── nginx.conf ├── file ├── go │ ├── benchmark.go │ └── benchmark.h ├── lua │ └── benchmark.lua └── node │ ├── app.js │ ├── config │ └── default.yaml │ └── package.json ├── example ├── example.go ├── example.h └── example.lua ├── lua └── lua2go.lua └── lua2go-scm-1.rockspec /.gitignore: -------------------------------------------------------------------------------- 1 | *.lock 2 | *.log.* 3 | *_temp 4 | 5 | ### Lua template 6 | # Compiled Lua sources 7 | luac.out 8 | 9 | # luarocks build files 10 | *.src.rock 11 | *.zip 12 | *.tar.gz 13 | 14 | # Object files 15 | *.o 16 | *.os 17 | *.ko 18 | *.obj 19 | *.elf 20 | 21 | # Precompiled Headers 22 | *.gch 23 | *.pch 24 | 25 | # Libraries 26 | *.lib 27 | *.a 28 | *.la 29 | *.lo 30 | *.def 31 | *.exp 32 | 33 | # Shared objects (inc. Windows DLLs) 34 | *.dll 35 | *.so 36 | *.so.* 37 | *.dylib 38 | 39 | # Executables 40 | *.exe 41 | *.out 42 | *.app 43 | *.i*86 44 | *.x86_64 45 | *.hex 46 | 47 | ### Node template 48 | # Logs 49 | logs 50 | *.log 51 | npm-debug.log* 52 | 53 | # Runtime data 54 | pids 55 | *.pid 56 | *.seed 57 | 58 | # Directory for instrumented libs generated by jscoverage/JSCover 59 | lib-cov 60 | 61 | # Coverage directory used by tools like istanbul 62 | coverage 63 | 64 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 65 | .grunt 66 | 67 | # node-waf configuration 68 | .lock-wscript 69 | 70 | # Compiled binary addons (http://nodejs.org/api/addons.html) 71 | build/Release 72 | 73 | # Dependency directory 74 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 75 | node_modules 76 | ### Go template 77 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 78 | *.o 79 | *.a 80 | *.so 81 | 82 | # Folders 83 | _obj 84 | _test 85 | 86 | # Architecture specific extensions/prefixes 87 | *.[568vq] 88 | [568vq].out 89 | 90 | *.cgo1.go 91 | *.cgo2.c 92 | _cgo_defun.c 93 | _cgo_gotypes.go 94 | _cgo_export.* 95 | 96 | _testmain.go 97 | 98 | *.exe 99 | *.test 100 | *.prof 101 | 102 | # Created by .ignore support plugin (hsz.mobi) 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Apigee Corp 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Accessing Go via Lua 2 | ==================== 3 | 4 | This module enables easy access to Go modules from LuaJit and therefore, NGINX with the LuaJit module. So if you need the capabilities of Go in your NGINX processing, you've come to the right place! 5 | 6 | Note: Lua2Go is now available from LuaRocks: http://luarocks.org/modules/scottganyo/lua2go 7 | 8 | To use (see also the [example](./example): 9 | 10 | 1. Write your Go module and `export` your functions: 11 | 12 | ``` 13 | //export add 14 | func add(operand1 int, operand2 int) int { 15 | return operand1 + operand2 16 | } 17 | ``` 18 | 19 | 2. Build your go module as a shared library: 20 | 21 | `go build -buildmode=c-shared -o example.so example.go` 22 | 23 | 3. Include a bit of setup in your Lua file: 24 | 25 | ``` 26 | local lua2go = require('lua2go') 27 | local example = lua2go.Load('./example.so') 28 | ``` 29 | 30 | 4. Register your `extern` declarations from your header file (`example.h`) in your Lua: 31 | 32 | ``` 33 | lua2go.Externs[[ 34 | extern GoInt add(GoInt p0, GoInt p1); 35 | ]] 36 | ``` 37 | 38 | 5. Call your Go function from Lua (see [example](./example) for more detail): 39 | 40 | ``` 41 | local result = lua2go.ToGo(example.add(1, 1)) 42 | ``` 43 | 44 | 6. Run your app: 45 | 46 | `luajit myapp.lua` 47 | 48 | 7. Bask in the glory of all that you've accomplished! 49 | 50 | 8. To see how Lua2Go can be incorporated into NGINX, check out the [benchmark](./benchmark) example. 51 | 52 | Enjoy! 53 | -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | Benchmarking Go via LuaJit in nginx 2 | =================================== 3 | 4 | 1. build the benchmark library 5 | 6 | ``` 7 | cd go 8 | go build -buildmode=c-shared -o benchmark.so benchmark.go 9 | cd .. 10 | ``` 11 | 12 | 2. install openresty (or nginx w/ LuaJit library) 13 | 14 | 3. add localhost aliases: 'local', 'proxy', and 'target' to your /etc/hosts 15 | 16 | 4. execute: `openresty -p $PWD -c conf/nginx.conf` 17 | 18 | 5. start node proxy (optional) 19 | 20 | ``` 21 | cd node 22 | npm install 23 | node app.js 24 | ``` 25 | 26 | 6. You will now have the following endpoints to hit for various benchmarks: 27 | 28 | `http://localhost:3000/echo` : nginx echo 29 | 30 | `http://localhost:3000/file` : nginx serve small static file 31 | 32 | `http://localhost:3000/lua2go` : nginx -> lua2go -> benchmark.go, echo result 33 | 34 | In addition, the following endpoints will proxy nginx -> nginx: 35 | 36 | `http://localhost:3001/echo` : nginx proxy_pass -> target nginx echo 37 | 38 | `http://localhost:3001/file` : nginx proxy_pass -> target nginx serve small static file 39 | 40 | `http://localhost:3001/lua2go` : nginx -> lua2go -> benchmark.go -> rewrite -> /echo 41 | 42 | And, if you ran the node proxy, the following endpoints will proxy node -> nginx: 43 | 44 | `http://localhost:3003/echo` : node -> nginx echo 45 | 46 | `http://localhost:3003/file` : nginx serve small static file 47 | 48 | 7. Run your own benchmarks! I don't have a script. :) 49 | 50 | You can install weighttp and try something like this: 51 | 52 | `weighttp -n 1000 -c 128 -t 128 -k -H "User-Agent: Me" localhost:3000/lua2go` 53 | -------------------------------------------------------------------------------- /benchmark/conf/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 4; 2 | 3 | error_log /dev/stderr; 4 | error_log error.log; 5 | 6 | events { 7 | worker_connections 1024; 8 | } 9 | 10 | http { 11 | 12 | # lua_code_cache off; 13 | lua_package_path "../lua/?.lua;;;"; 14 | 15 | upstream target { 16 | server localhost:3002; 17 | } 18 | 19 | init_by_lua_block { 20 | ffi = require('ffi') 21 | lua2go = require('lua2go') 22 | 23 | -- copied from benchmark.h 24 | ffi.cdef[[ 25 | extern GoString process(GoString method, GoString headers, GoString body); 26 | ]] 27 | } 28 | 29 | root ./; 30 | 31 | # responds from the local (initial) nginx 32 | server { 33 | server_name localhost; 34 | listen 3000; 35 | 36 | # for static page call /file 37 | location / { 38 | } 39 | 40 | location /echo { 41 | echo local_; 42 | } 43 | 44 | location /lua2go { 45 | content_by_lua_block { 46 | local benchmark = require('lua/benchmark') 47 | local result = benchmark.run() 48 | ngx.say(result) 49 | } 50 | } 51 | } 52 | 53 | # responds by proxying to the "target" nginx 54 | server { 55 | server_name localhost; 56 | listen 3001; 57 | 58 | location / { 59 | proxy_pass http://target; 60 | } 61 | 62 | location /echo { 63 | proxy_pass http://target/echo; 64 | } 65 | 66 | location /lua2go { 67 | rewrite_by_lua_block { 68 | local benchmark = require('lua/benchmark') 69 | local result = benchmark.run() 70 | -- result = ngx.location.capture('/echo') 71 | -- ngx.say(result.body) 72 | ngx.req.set_uri('/echo', true) 73 | } 74 | } 75 | } 76 | 77 | # the "target" nginx is an echo server 78 | server { 79 | server_name localhost; 80 | listen 3002; 81 | 82 | # for static page call /file 83 | location / { 84 | } 85 | 86 | location /echo { 87 | echo target; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /benchmark/file: -------------------------------------------------------------------------------- 1 | afile 2 | -------------------------------------------------------------------------------- /benchmark/go/benchmark.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "C" 4 | import "strconv" 5 | 6 | var counter = 0 7 | 8 | //export process 9 | func process(method string, headers string, body string) string { 10 | counter = counter + 1 11 | return "lua2go" + strconv.Itoa(counter) 12 | } 13 | 14 | func main() {} 15 | -------------------------------------------------------------------------------- /benchmark/go/benchmark.h: -------------------------------------------------------------------------------- 1 | /* Created by "go tool cgo" - DO NOT EDIT. */ 2 | 3 | /* package command-line-arguments */ 4 | 5 | /* Start of preamble from import "C" comments. */ 6 | 7 | 8 | 9 | 10 | /* End of preamble from import "C" comments. */ 11 | 12 | 13 | /* Start of boilerplate cgo prologue. */ 14 | 15 | #ifndef GO_CGO_PROLOGUE_H 16 | #define GO_CGO_PROLOGUE_H 17 | 18 | typedef signed char GoInt8; 19 | typedef unsigned char GoUint8; 20 | typedef short GoInt16; 21 | typedef unsigned short GoUint16; 22 | typedef int GoInt32; 23 | typedef unsigned int GoUint32; 24 | typedef long long GoInt64; 25 | typedef unsigned long long GoUint64; 26 | typedef GoInt64 GoInt; 27 | typedef GoUint64 GoUint; 28 | typedef __SIZE_TYPE__ GoUintptr; 29 | typedef float GoFloat32; 30 | typedef double GoFloat64; 31 | typedef __complex float GoComplex64; 32 | typedef __complex double GoComplex128; 33 | 34 | // static assertion to make sure the file is being used on architecture 35 | // at least with matching size of GoInt. 36 | typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; 37 | 38 | typedef struct { char *p; GoInt n; } GoString; 39 | typedef void *GoMap; 40 | typedef void *GoChan; 41 | typedef struct { void *t; void *v; } GoInterface; 42 | typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; 43 | 44 | #endif 45 | 46 | /* End of boilerplate cgo prologue. */ 47 | 48 | #ifdef __cplusplus 49 | extern "C" { 50 | #endif 51 | 52 | 53 | extern GoString process(GoString p0, GoString p1, GoString p2); 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /benchmark/lua/benchmark.lua: -------------------------------------------------------------------------------- 1 | -- called from nginx.conf 2 | 3 | local benchmark = {} 4 | 5 | function benchmark.run() 6 | 7 | -- note: apigee externs are defined in nginx.confg 8 | local benchmark = lua2go.Load('./go/benchmark.so') 9 | 10 | local method = ngx.req.get_method() 11 | 12 | local no_request_line = true 13 | local rawHeaders = ngx.req.raw_header(no_request_line) 14 | 15 | local body = ngx.req.get_body_data() or '' -- cannot be nil 16 | 17 | local goResult = benchmark.process(lua2go.ToGo(method), lua2go.ToGo(rawHeaders), lua2go.ToGo(body)) 18 | 19 | return lua2go.ToLua(goResult) 20 | end 21 | 22 | return benchmark 23 | -------------------------------------------------------------------------------- /benchmark/node/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const connect = require('connect') 4 | const http = require('http') 5 | const debug = require('debug')('test') 6 | const microgateway = require('microgateway-core') 7 | const config = require('config') 8 | 9 | const gateway = microgateway(config) 10 | 11 | debug('starting gateway') 12 | 13 | gateway.start((err, server) => { 14 | if (err) { 15 | debug('gateway err %o', err) 16 | process.exit(1) 17 | } 18 | 19 | debug('gateway started') 20 | }) 21 | -------------------------------------------------------------------------------- /benchmark/node/config/default.yaml: -------------------------------------------------------------------------------- 1 | edgemicro: 2 | port: 3003 3 | logging: 4 | level: warn 5 | proxies: 6 | - base_path: / 7 | url: http://localhost:3002 8 | -------------------------------------------------------------------------------- /benchmark/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "microproxy", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "app.js", 7 | "author": "Scott Ganyo", 8 | "license": "MIT", 9 | "dependencies": { 10 | "config": "^1.19.0", 11 | "connect": "^3.3.5", 12 | "debug": "^2.2.0", 13 | "js-yaml": "^3.5.5", 14 | "microgateway-core": "git@github.com:apigee/microgateway-core.git" 15 | }, 16 | "scripts": { 17 | "start": "node app.js" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "C" 5 | "strings" 6 | ) 7 | 8 | 9 | //export add 10 | func add(operand1 int, operand2 int) int { 11 | return operand1 + operand2 12 | } 13 | 14 | //export concat 15 | func concat(elements []string, separator string) *C.char { 16 | // This CString must be released by caller! 17 | return C.CString(strings.Join(elements, separator)) 18 | } 19 | 20 | //export increment 21 | func increment(value *int) { 22 | *value = *value + 1 23 | } 24 | 25 | //export reverse 26 | func reverse(value []int) { 27 | for i, j := 0, len(value) - 1; i < j; i, j = i + 1, j - 1 { 28 | value[i], value[j] = value[j], value[i] 29 | } 30 | } 31 | 32 | func main() {} 33 | -------------------------------------------------------------------------------- /example/example.h: -------------------------------------------------------------------------------- 1 | /* Created by "go tool cgo" - DO NOT EDIT. */ 2 | 3 | /* package command-line-arguments */ 4 | 5 | /* Start of preamble from import "C" comments. */ 6 | 7 | 8 | 9 | 10 | /* End of preamble from import "C" comments. */ 11 | 12 | 13 | /* Start of boilerplate cgo prologue. */ 14 | 15 | #ifndef GO_CGO_PROLOGUE_H 16 | #define GO_CGO_PROLOGUE_H 17 | 18 | typedef signed char GoInt8; 19 | typedef unsigned char GoUint8; 20 | typedef short GoInt16; 21 | typedef unsigned short GoUint16; 22 | typedef int GoInt32; 23 | typedef unsigned int GoUint32; 24 | typedef long long GoInt64; 25 | typedef unsigned long long GoUint64; 26 | typedef GoInt64 GoInt; 27 | typedef GoUint64 GoUint; 28 | typedef __SIZE_TYPE__ GoUintptr; 29 | typedef float GoFloat32; 30 | typedef double GoFloat64; 31 | typedef float _Complex GoComplex64; 32 | typedef double _Complex GoComplex128; 33 | 34 | /* 35 | static assertion to make sure the file is being used on architecture 36 | at least with matching size of GoInt. 37 | */ 38 | typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; 39 | 40 | typedef struct { const char *p; GoInt n; } GoString; 41 | typedef void *GoMap; 42 | typedef void *GoChan; 43 | typedef struct { void *t; void *v; } GoInterface; 44 | typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; 45 | 46 | #endif 47 | 48 | /* End of boilerplate cgo prologue. */ 49 | 50 | #ifdef __cplusplus 51 | extern "C" { 52 | #endif 53 | 54 | 55 | extern GoInt add(GoInt p0, GoInt p1); 56 | 57 | extern char* concat(GoSlice p0, GoString p1); 58 | 59 | extern void increment(GoInt* p0); 60 | 61 | extern void reverse(GoSlice p0); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | -------------------------------------------------------------------------------- /example/example.lua: -------------------------------------------------------------------------------- 1 | -- ensure the lua2go lib is on the LUA_PATH so it will load 2 | -- normally, you'd just put it on the LUA_PATH 3 | package.path = package.path .. ';../lua/?.lua' 4 | 5 | -- load lua2go 6 | local lua2go = require('lua2go') 7 | 8 | -- load my Go library 9 | local example = lua2go.Load('./example.so') 10 | 11 | -- copy just the extern functions from benchmark.h into ffi.cdef structure below 12 | -- (the boilerplate cgo prologue is already defined for you in lua2go) 13 | -- this registers your Go functions to the ffi library.. 14 | lua2go.Externs[[ 15 | extern GoInt add(GoInt p0, GoInt p1); 16 | extern char* concat(GoSlice p0, GoString p1); 17 | extern void increment(GoInt* p0); 18 | extern void reverse(GoSlice p0); 19 | ]] 20 | 21 | --------------------------------------------------------- 22 | -- all set up, now you may call your Go functions! 23 | 24 | --------------------------------------------------------- 25 | -- Let's do the easiest one first. We'll add two ints and return the result. 26 | 27 | -- note that Lua numbers in the parameters are auto-converted to GoInts (yay!) 28 | -- but... the result will still be a GoInt... 29 | local goAddResult = example.add(1, 1) 30 | 31 | -- which means we need to convert it - easy with a call to lua2go.ToLua() 32 | -- we use capitalized exported functions, so you Go folks feel right at home! 33 | local addResult = lua2go.ToLua(goAddResult) 34 | 35 | print('1 + 1 = ' .. addResult) 36 | 37 | 38 | --------------------------------------------------------- 39 | -- What about passing an int by reference? Also, super easy! 40 | 41 | local value = 2 42 | 43 | -- create a Go pointer to the value 44 | local intPtr = lua2go.ToGoPointer(value) 45 | 46 | -- call increment, passing the pointer 47 | example.increment(intPtr) 48 | 49 | -- use [0] to deference the pointer 50 | local newValue = lua2go.ToLua(intPtr[0]) 51 | 52 | print(value .. ' + 1 = ' .. newValue) 53 | 54 | 55 | -------------------------------------------------------------------- 56 | -- Numbers are easy, but what about passing a Go Slice by reference? 57 | -- We'll reverse a Slice of numbers 58 | 59 | -- Note: Go Slice support is rudimentory right now and subject to change 60 | 61 | -- we'll reverse these values 62 | local values = { 1, 2, 3 } 63 | 64 | -- hold on to both results 65 | -- we'll give Go the slice, but we need the array for iteration 66 | local goSlice, goArray = lua2go.ToGoSlice(values) 67 | 68 | -- pass the slice to Go to reverse 69 | example.reverse(goSlice) 70 | 71 | -- print the reversed array 72 | -- note: this is a Go Array, so it's zero-indexed 73 | local countDown = '' 74 | for i = 0, #values - 1 do 75 | countDown = countDown .. ' ...' .. lua2go.ToLua(goArray[i]) 76 | end 77 | print("The final countdown" .. countDown) 78 | 79 | 80 | -------------------------------------------------------------------------- 81 | -- Finally, let's concat an table together and return a new string from Go 82 | 83 | -- we'll start with a table of strings 84 | local strings = { 85 | 'Lua2Go', 86 | 'is', 87 | 'pretty', 88 | 'cool!' 89 | } 90 | 91 | -- then convert the table to a slice of Go strings 92 | local goSliceOfStrings = lua2go.ToGoSlice(strings) 93 | 94 | -- make a GoString for the separator 95 | local separator = lua2go.ToGo(' ') 96 | 97 | -- call our Go concat function 98 | local result = example.concat(goSliceOfStrings, separator) 99 | 100 | -- Important: Go allocated the return value, so we'll need to deallocate it! 101 | -- tell the Lua garbage collector to handle this for us 102 | lua2go.AddToGC(result) 103 | 104 | -- convert to Lua 105 | local luaConcatResult = lua2go.ToLua(result) 106 | 107 | print(luaConcatResult) 108 | -------------------------------------------------------------------------------- /lua/lua2go.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | A library for calling into Go from LuaJit 4 | 5 | --]] 6 | 7 | local lua2go = {} 8 | 9 | local ffi = require('ffi') 10 | 11 | ffi.cdef[[ 12 | // standard Go definitions // 13 | 14 | typedef signed char GoInt8; 15 | typedef unsigned char GoUint8; 16 | typedef short GoInt16; 17 | typedef unsigned short GoUint16; 18 | typedef int GoInt32; 19 | typedef unsigned int GoUint32; 20 | typedef long long GoInt64; 21 | typedef unsigned long long GoUint64; 22 | typedef GoInt64 GoInt; 23 | typedef GoUint64 GoUint; 24 | typedef float GoFloat32; 25 | typedef double GoFloat64; 26 | typedef __complex float GoComplex64; 27 | typedef __complex double GoComplex128; 28 | 29 | // static assertion to make sure the file is being used on architecture 30 | // at least with matching size of GoInt. 31 | typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; 32 | 33 | // change to Go struct: add 'const' declaration to char * (required by Lua FFI) 34 | // typedef struct { char *p; GoInt n; } GoString; 35 | typedef struct { const char *p; GoInt n; } GoString; 36 | 37 | typedef void *GoMap; 38 | typedef void *GoChan; 39 | typedef struct { void *t; void *v; } GoInterface; 40 | typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; 41 | 42 | // C stdlib definitions // 43 | 44 | void free(void *ptr); 45 | ]] 46 | 47 | local makeGoString = ffi.typeof('GoString') 48 | local makeGoSlice = ffi.typeof('GoSlice') 49 | local makeGoStringArray = ffi.typeof('GoString[?]') 50 | local makeGoIntArray = ffi.typeof('GoInt[?]') 51 | local identity = function(x) return x end 52 | 53 | -- types: "nil", "number", "string", "boolean", "table", "function", "thread", or "userdata" 54 | local goTypes = { 55 | number = 'GoInt', 56 | string = 'GoString' 57 | } 58 | 59 | local goArrayConstructors = { 60 | number = makeGoIntArray, 61 | string = makeGoStringArray 62 | } 63 | 64 | local goConverters = { 65 | number = identity 66 | } 67 | goConverters['nil'] = identity 68 | 69 | 70 | -- 71 | -- public interface 72 | -- 73 | 74 | -- create cdata object 75 | lua2go.New = ffi.new 76 | 77 | -- add cdata to garbage collector, returns GC ref 78 | lua2go.AddToGC = function(cdata) 79 | return ffi.gc(cdata, ffi.C.free) 80 | end 81 | 82 | -- returns the typeof the Go object 83 | lua2go.GoType = ffi.typeof 84 | 85 | -- loads and returns a Go library 86 | lua2go.Load = ffi.load 87 | 88 | -- defines loaded functions 89 | lua2go.Externs = ffi.cdef 90 | 91 | -- convert C String to Lua 92 | lua2go.ToLuaString = function(str) 93 | if ffi.typeof(str) == makeGoString then 94 | return ffi.string(str.p) 95 | else 96 | return ffi.string(str) -- assume char * 97 | end 98 | end 99 | 100 | -- convert Number to Lua 101 | lua2go.ToLuaNumber = tonumber 102 | 103 | -- converts Lua String to a Go String 104 | function lua2go.ToGoString(str) 105 | if str == nil then return '' end 106 | return makeGoString({ str, #str }) 107 | end 108 | goConverters['string'] = lua2go.ToGoString 109 | 110 | -- returns the Go type a luaVar will map to 111 | -- currently supports Go strings and ints 112 | function lua2go.Type(luaVar) 113 | local luaType = type(luaVar) 114 | return goTypes[luaType] 115 | end 116 | 117 | -- retrieve a function to get the slice type for a lua table 118 | -- note: must be an array-style table, not a map 119 | function lua2go.SliceType(table) 120 | return lua2go.Type(table[1])..'[?]' 121 | end 122 | 123 | -- will make a Go array that is either Ints or Strings based on the first element type 124 | -- must be an array-style table, not a map 125 | -- currently only supports string and ints 126 | function lua2go.ToGoArray(table) 127 | local luaType = type(table[1]) 128 | local makeGoArray = goArrayConstructors[luaType] 129 | local goArray = makeGoArray(#table) 130 | local toGoType = goConverters[luaType] 131 | for index, value in next, table do 132 | goArray[index - 1] = toGoType(value) 133 | end 134 | return goArray 135 | end 136 | goConverters['table'] = lua2go.ToGoArray 137 | 138 | 139 | -- will make a Go slice that is either Ints or Strings based on the first element type 140 | -- must be an array-style table, not a map 141 | -- returns GoSlice, backing array 142 | function lua2go.ToGoSlice(table) 143 | local goArray = lua2go.ToGoArray(table) 144 | return makeGoSlice({ goArray, #table, #table }), goArray 145 | end 146 | 147 | -- retrieve a function to convert the luaVar to a goVar based on the type of luaVar 148 | -- currently supports Go strings and ints 149 | function lua2go.Converter(luaVar) 150 | return goConverters[type(luaVar)] 151 | end 152 | 153 | -- converts a goVar to a luaVar 154 | -- currently supports Go strings and ints 155 | function lua2go.ToLua(goVar) 156 | if goVar == nil then return nil end 157 | if ffi.istype('GoString', goVar) or ffi.istype('char *', goVar) then 158 | return lua2go.ToLuaString(goVar) 159 | elseif ffi.istype('GoInt', goVar) then 160 | return lua2go.ToLuaNumber(goVar) 161 | else 162 | error('unknown type') 163 | end 164 | end 165 | 166 | -- converts luaVar to goVar using simple type mapping 167 | -- currently only converts strings, numbers are left alone as they are converted automatically 168 | function lua2go.ToGo(luaVar) 169 | local convert = lua2go.Converter(luaVar) 170 | return convert(luaVar) 171 | end 172 | 173 | -- returns a pointer and a value holder variable 174 | function lua2go.ToGoPointer(luaVar) 175 | local luaType = type(luaVar) 176 | local makeGoArray = goArrayConstructors[luaType] 177 | local ptr = makeGoArray(1, luaVar) 178 | return ptr, ptr[0] 179 | end 180 | 181 | local module_mt = { 182 | __newindex = (function (table, key, val) error('Attempt to write to undeclared variable "' .. key .. '"') end) 183 | } 184 | setmetatable(lua2go, module_mt) 185 | return lua2go 186 | -------------------------------------------------------------------------------- /lua2go-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Lua2Go" 2 | version = "scm-1" 3 | source = { 4 | url = "git://github.com/theganyo/lua2go" 5 | } 6 | description = { 7 | summary = "Easy access to your Go (Golang) modules from Lua and NGINX", 8 | detailed = [[ 9 | This module enables easy access to Go modules from LuaJit and therefore, 10 | NGINX with the LuaJit module. So if you need the capabilities of Go in 11 | your NGINX processing, you've come to the right place! 12 | ]], 13 | homepage = "https://github.com/theganyo/lua2go", 14 | license = "Apache-2.0" 15 | } 16 | dependencies = { 17 | "lua ~> 5.1" 18 | } 19 | build = { 20 | type = "builtin", 21 | modules = { 22 | lua2go = "lua/lua2go.lua" 23 | } 24 | } --------------------------------------------------------------------------------