[
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 Andrey Petrov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "Makefile",
    "content": "OS  = $(shell uname -s)\n\nifeq ($(OS), Darwin)\n\tLDFLAGS += -framework CoreFoundation -framework Security\nendif\n\nall: examples\n\nexamples: example-c example-python\n\nexample-c: build/gohttp-c\n\nexample-c-static: build/gohttp-c-static\n\nexample-python: examples/python/gohttp/_gohttplib.py\n\n\nbuild/:\n\tmkdir build\n\nbuild/libgohttp.a: *.go\n\tgo build -buildmode=c-archive -o $@\n\nbuild/libgohttp.so: *.go build\n\tgo build -buildmode=c-shared -o libgohttp.so && mv libgohttp.* $(dir $@)\n\nbuild/gohttp-c-static: examples/c/ build/libgohttp.a\n\tgcc -o $@ examples/c/main.c build/libgohttp.a $(LDFLAGS) -lpthread\n\nbuild/gohttp-c: examples/c/ build/libgohttp.so\n\tgcc -o $@ examples/c/main.c -Lbuild -lgohttp -lpthread $(LDFLAGS)\n\nexamples/python/gohttp/libgohttp.so: build/libgohttp.so\n\tcp $< $@\n\nexamples/python/gohttp/_gohttplib.py: examples/python/gohttp/build_gohttplib.py examples/python/gohttp/libgohttp.so\n\tcd examples/python && python gohttp/build_gohttplib.py\n\nclean:\n\trm -rf build/\n\nbenchmark:\n\tab -n 10000 -c 10 -s 3 http://127.0.0.1:$(PORT)/\n"
  },
  {
    "path": "README.md",
    "content": "# gohttplib\n\nShared library that exposes Go's `net/http.Server` with externally-bindable\nhandlers.\n\nThis is a silly project for experimenting with Go buildmodes. I gave a [talk about this at PyCon 2016](https://www.youtube.com/watch?v=CkDwb5koRTc).\n\n**Status**: Tiny subset of the `http.HandlerFunc` callback gets passed to a C\nhandler callback. Python bindings are working, too.\n\n\n## Getting Started\n\nRequirements:\n\n- Go 1.5 or newer.\n- C example: make, gcc\n- Python example: cffi, python-cffi\n\n```\n$ git clone https://github.com/shazow/gohttplib/\n$ cd gohttplib\n$ make examples\n```\n\n### Example: C\n\nC example can be linked against a shared object (`libgohttp.so` generated by\n`go build -buildmode=c-shared`) or against a static library archive\n(`libgohttp.a` generated by `go build -buildmode=c-archive`). By default, we\nlink against the shared object because that's what the Python example uses too.\n\nLinked against our shared object:\n\n```\n$ make example-c\n$ DYLD_LIBRARY_PATH=build/ ./build/gohttp-c\n```\n\nNote that you'll need to make sure that `libgohttp.so` is findable at runtime.\n\nNow you can request `http://localhost:8000/hello` and the C handler in\n`examples/c/main.c` will handle it!\n\nLinked against our static library archive:\n\n```\n$ make example-c-static\n$ ./build/gohttp-c-static\n```\n\nThe static archive gets built into the binary so it's more portable during\nruntime. Note the size differences:\n\n```\n8.8K  gohttp-c\n5.1M  gohttp-c-static\n7.5M  libgohttp.a\n6.9M  libgohttp.so\n```\n\n### Example: Python\n\nThe Python example uses [python-cffi](https://cffi.readthedocs.org/en/latest/)\n(you'll need python-cffi installed) to link against the shared object.\n\n```\n$ make example-python\n$ cd examples/python\n$ python -m gohttp\n * Running on http://127.0.0.1:5000/\n```\n\nOr write your own handler using this library:\n\n```python\nfrom gohttp import route, run\n\n@route('/')\ndef index(w, req):\n    w.write(\"%s %s %s\\n\" % (req.method, req.host, req.url))\n    w.write(\"Hello, world.\\n\")\n\nrun(host='127.0.0.1', port=5000)\n```\n\n\n## References & Credit\n\n* [Go Execution Modes](https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/view#) document.\n* Every single StackOverflow thread tagged [cgo].\n* [github.com/jrick/buildmodes](https://github.com/jrick/buildmodes)\n* [github.com/wolever/python-cffi-example](https://github.com/wolever/python-cffi-example/)\n* Invaluable help with python-cffi from [@wolever](https://twitter.com/wolever),\n  [@paultag](https://twitter.com/paultag), and\n  [@dstufft](https://twitter.com/dstufft).\n\n\n## Sponsors\n\nThis project was made possible thanks to [Glider Labs](http://gliderlabs.com/).\n\n\n## License\n\nMIT\n"
  },
  {
    "path": "examples/c/main.c",
    "content": "#include <stdio.h>\n#include \"../../build/libgohttp.h\"\n\nvoid handler(ResponseWriterPtr w, Request *req)\n{\n    printf(\"handler: %s %s\\n%s\\n\\n%s\\n\", req->Method, req->URL, req->Headers, req->Body);\n\n    char *buf = \"Hello, world\";\n    int n = ResponseWriter_Write(w, buf, 12);\n\n    if (n == EOF) {\n        printf(\"handler: Failed to write.\\n\");\n        ResponseWriter_WriteHeader(w, 500);\n        return;\n    }\n\n    printf(\"handler: Wrote %d bytes.\\n\", n);\n}\n\nint main()\n{\n    HandleFunc(\"/\", &handler);\n    printf(\"Listening on :8000\\n\");\n    ListenAndServe(\":8000\");\n    return 0;\n}\n"
  },
  {
    "path": "examples/python/MANIFEST.in",
    "content": "include README.rst\ninclude gohttp/*.so\n"
  },
  {
    "path": "examples/python/Makefile",
    "content": "dist/: build\n\nbuild:\n\tpython setup.py sdist bdist\n\npublish: dist/\n\ttwine upload dist/*\n\nclean:\n\trm -rf build/ dist/\n"
  },
  {
    "path": "examples/python/README.rst",
    "content": "gohttp: Bindings for gohttplib\n==============================\n\n*Warning*: This library currently ships with a shared object of gohttplib\ncompiled for OSX. It will not work on other platforms at the moment.\n\nSee `<https://github.com/shazow/gohttplib>`_ for details.\n\n\nUsage\n-----\n\n::\n\n    from gohttp import route, run\n    \n    @route('/')\n    def index(w, req):\n        w.write(\"%s %s %s\\n\" % (req.method, req.host, req.url))\n        w.write(\"Hello, world.\\n\")\n    \n    run(host='127.0.0.1', port=5000)\n\n\nLicense\n-------\n\nMIT.\n"
  },
  {
    "path": "examples/python/gohttp/__init__.py",
    "content": "import os\nfrom ._gohttplib import ffi\n\nlib = ffi.dlopen(os.path.join(os.path.dirname(__file__), \"libgohttp.so\"))\n\n# Hang onto handlers so they don't get gc'd\n_handlers = []\n\n\nclass ResponseWriter:\n    def __init__(self, w):\n        self._w = w\n\n    def write(self, body):\n        n = lib.ResponseWriter_Write(self._w, body, len(body))\n        if n != len(body):\n            raise IOError(\"Failed to write to ResponseWriter.\")\n\n    def set_status(self, code):\n        lib.ResponseWriter_WriteHeader(self._w, code)\n\n\nclass Request:\n    def __init__(self, req):\n        self._req = req\n\n    @property\n    def method(self):\n        return ffi.string(self._req.Method)\n\n    @property\n    def host(self):\n        return ffi.string(self._req.Host)\n\n    @property\n    def url(self):\n        return ffi.string(self._req.URL)\n\n    @property\n    def body(self):\n        return ffi.string(self._req.Body)\n\n    @property\n    def headers(self):\n        return ffi.string(self._req.Headers)\n\n    def __repr__(self):\n        return \"{self.method} {self.url}\".format(self=self)\n\n\ndef route(pattern, fn=None):\n    \"\"\"\n    Can be used as a decorator.\n\n    :param pattern:\n        Address pattern to match against.\n\n    :param fn:\n        Handler to call when pattern is matched. Handler is given a\n        ResponseWriter and Request object.\n    \"\"\"\n    def wrapped(fn):\n        @ffi.callback(\"void(ResponseWriterPtr, Request*)\")\n        def handler(w, req):\n            fn(ResponseWriter(w), Request(req))\n\n        lib.HandleFunc(str.encode(pattern), handler)\n        _handlers.append(handler)\n\n    if fn:\n        return wrapped(fn)\n\n    return wrapped\n\n\ndef run(host='', port=5000):\n    bind = '{}:{}'.format(host or '', port)\n    print(\" * Running on http://{}/\".format(bind))\n    lib.ListenAndServe(str.encode(bind))\n    \ndef shutdown():\n    lib.Shutdown()\n"
  },
  {
    "path": "examples/python/gohttp/__main__.py",
    "content": "from . import route, run\n\nif __name__ == '__main__':\n    @route('/')\n    def index(w, req):\n        w.write(b\"%s %s %s\\n%s\\n\\n%s\\n\\n\" % (req.method, req.host, req.url, req.headers, req.body))\n        w.write(b\"Hello, world.\\n\")\n\n    run()\n"
  },
  {
    "path": "examples/python/gohttp/_gohttplib.py",
    "content": "# auto-generated file\nimport _cffi_backend\n\nffi = _cffi_backend.FFI('gohttp._gohttplib',\n    _version = 0x2601,\n    _types = b'\\x00\\x00\\x03\\x0D\\x00\\x00\\x08\\x01\\x00\\x00\\x1D\\x03\\x00\\x00\\x07\\x01\\x00\\x00\\x00\\x0F\\x00\\x00\\x1E\\x0D\\x00\\x00\\x02\\x11\\x00\\x00\\x00\\x0F\\x00\\x00\\x1E\\x0D\\x00\\x00\\x02\\x11\\x00\\x00\\x0C\\x03\\x00\\x00\\x00\\x0F\\x00\\x00\\x1E\\x0D\\x00\\x00\\x08\\x01\\x00\\x00\\x1B\\x03\\x00\\x00\\x00\\x0F\\x00\\x00\\x1E\\x0D\\x00\\x00\\x08\\x01\\x00\\x00\\x0E\\x11\\x00\\x00\\x0A\\x11\\x00\\x00\\x00\\x0F\\x00\\x00\\x1E\\x0D\\x00\\x00\\x08\\x01\\x00\\x00\\x07\\x01\\x00\\x00\\x00\\x0F\\x00\\x00\\x1E\\x0D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x09\\x00\\x00\\x1D\\x03\\x00\\x00\\x02\\x01\\x00\\x00\\x00\\x01',\n    _globals = (b'\\x00\\x00\\x10\\x23Call_HandleFunc',0,b'\\x00\\x00\\x08\\x23HandleFunc',0,b'\\x00\\x00\\x05\\x23ListenAndServe',0,b'\\x00\\x00\\x00\\x23ResponseWriter_Write',0,b'\\x00\\x00\\x15\\x23ResponseWriter_WriteHeader',0,b'\\x00\\x00\\x19\\x23Shutdown',0),\n    _struct_unions = ((b'\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x02Request_',b'\\x00\\x00\\x1C\\x11Method',b'\\x00\\x00\\x1C\\x11Host',b'\\x00\\x00\\x1C\\x11URL',b'\\x00\\x00\\x1C\\x11Body',b'\\x00\\x00\\x1C\\x11Headers'),),\n    _typenames = (b'\\x00\\x00\\x00\\x0CFuncPtr',b'\\x00\\x00\\x00\\x1BRequest',b'\\x00\\x00\\x00\\x01ResponseWriterPtr'),\n)\n"
  },
  {
    "path": "examples/python/gohttp/build_gohttplib.py",
    "content": "from cffi import FFI\n\nffi = FFI()\nffi.set_source(\"gohttp._gohttplib\", None)\n\n# Copied from the Go-generated gohttplib.h\nffi.cdef(\"\"\"\ntypedef struct Request_\n{\n    const char *Method;\n    const char *Host;\n    const char *URL;\n    const char *Body;\n    const char *Headers;\n} Request;\n\ntypedef unsigned int ResponseWriterPtr;\n\ntypedef void FuncPtr(ResponseWriterPtr w, Request *r);\n\nvoid Call_HandleFunc(ResponseWriterPtr w, Request *r, FuncPtr *fn);\n\nvoid ListenAndServe(char* p0);\n\nvoid Shutdown();\n\nvoid HandleFunc(char* p0, FuncPtr* p1);\n\nint ResponseWriter_Write(unsigned int p0, char* p1, int p2);\n\nvoid ResponseWriter_WriteHeader(unsigned int p0, int p1);\n\"\"\")\n\n\nif __name__ == \"__main__\":\n    ffi.compile()\n"
  },
  {
    "path": "examples/python/setup.py",
    "content": "#!/usr/bin/env python\n\nimport os\nimport sys\n\nfrom setuptools import setup, find_packages\n\nos.chdir(os.path.dirname(sys.argv[0]) or \".\")\n\nsetup(\n    name=\"gohttp\",\n    version=\"0.3.3\",\n    description=\"Bindings for gohttplib, exposing Go's http.Server\",\n    long_description=open(\"README.rst\", \"rt\").read(),\n    url=\"https://github.com/shazow/gohttplib\",\n    author=\"Andrey Petrov\",\n    author_email=\"andrey.petrov@shazow.net\",\n    classifiers=[\n        \"Programming Language :: Python :: 2\",\n        \"Programming Language :: Python :: 3\",\n        \"License :: OSI Approved :: MIT License\",\n    ],\n    packages=find_packages(),\n    install_requires=[\"cffi>=1.0.0\"],\n    setup_requires=[\"cffi>=1.0.0\"],\n    cffi_modules=[\n        \"./gohttp/build_gohttplib.py:ffi\",\n    ],\n    package_data={\n        \"gohttp\": [\"libgohttp.so\"],\n    },\n)\n"
  },
  {
    "path": "gohttplib.c",
    "content": "#include \"_cgo_export.h\"\n\nvoid Call_HandleFunc(ResponseWriterPtr w, Request *req, FuncPtr *fn) {\n    return fn(w, req);\n} \n"
  },
  {
    "path": "gohttplib.go",
    "content": "package main\n\n/*\n#include <stdlib.h>\n\ntypedef struct Request_\n{\n    const char *Method;\n    const char *Host;\n\t\tconst char *URL;\n\t\tconst char *Body;\n    const char *Headers;\n} Request;\n\ntypedef unsigned int ResponseWriterPtr;\n\ntypedef void FuncPtr(ResponseWriterPtr w, Request *r);\n\nextern void Call_HandleFunc(ResponseWriterPtr w, Request *r, FuncPtr *fn);\n*/\nimport \"C\"\nimport (\n\t\"bytes\"\n\t\"net/http\"\n\t\"unsafe\"\n\t\"context\"\n)\n\nvar cpointers = PtrProxy()\nvar srv http.Server = http.Server{}\n\n//export ListenAndServe\nfunc ListenAndServe(caddr *C.char) {\n\taddr := C.GoString(caddr)\n\tsrv.Addr = addr\n\tsrv.ListenAndServe()\n}\n\n//export Shutdown\nfunc Shutdown() {\n\tsrv.Shutdown(context.Background())\n}\n\n//export HandleFunc\nfunc HandleFunc(cpattern *C.char, cfn *C.FuncPtr) {\n\t// C-friendly wrapping for our http.HandleFunc call.\n\tpattern := C.GoString(cpattern)\n\thttp.HandleFunc(pattern, func(w http.ResponseWriter, req *http.Request) {\n\t\t// Convert the headers to a String\n\t\theaderBuffer := new(bytes.Buffer)\n\t\treq.Header.Write(headerBuffer)\n\t\theadersString := headerBuffer.String()\n\t\t// Convert the request body to a String\n\t\tbodyBuffer := new(bytes.Buffer)\n\t\tbodyBuffer.ReadFrom(req.Body)\n\t\tbodyString := bodyBuffer.String()\n\t\t// Wrap relevant request fields in a C-friendly datastructure.\n\t\tcreq := C.Request{\n\t\t\tMethod:  C.CString(req.Method),\n\t\t\tHost:    C.CString(req.Host),\n\t\t\tURL:     C.CString(req.URL.String()),\n\t\t\tBody:    C.CString(bodyString),\n\t\t\tHeaders: C.CString(headersString),\n\t\t}\n\t\t// Convert the ResponseWriter interface instance to an opaque C integer\n\t\t// that we can safely pass along.\n\t\twPtr := cpointers.Ref(unsafe.Pointer(&w))\n\t\t// Call our C function pointer using our C shim.\n\t\tC.Call_HandleFunc(C.ResponseWriterPtr(wPtr), &creq, cfn)\n\t\t// release the C memory\n\t\tC.free(unsafe.Pointer(creq.Method))\n\t\tC.free(unsafe.Pointer(creq.Host))\n\t\tC.free(unsafe.Pointer(creq.URL))\n\t\tC.free(unsafe.Pointer(creq.Body))\n\t\tC.free(unsafe.Pointer(creq.Headers))\n\t\t// Release the ResponseWriter from the registry since we're done with\n\t\t// this response.\n\t\tcpointers.Free(wPtr)\n\t})\n}\n\nfunc main() {}\n"
  },
  {
    "path": "ptrproxy.go",
    "content": "package main\n\nimport (\n\t\"C\"\n\t\"sync\"\n\t\"unsafe\"\n)\n\n// PtrProxy creates a safe pointer registry. It hangs on to an unsafe.Pointer and\n// returns a totally-safe C.uint ID that can be used to look up the original\n// pointer by using it.\nfunc PtrProxy() *ptrProxy {\n\treturn &ptrProxy{\n\t\tlookup: map[uint]unsafe.Pointer{},\n\t}\n}\n\ntype ptrProxy struct {\n\tsync.Mutex\n\tcount  uint\n\tlookup map[uint]unsafe.Pointer\n}\n\n// Ref registers the given pointer and returns a corresponding id that can be\n// used to retrieve it later.\nfunc (p *ptrProxy) Ref(ptr unsafe.Pointer) C.uint {\n\tp.Lock()\n\tid := p.count\n\tp.count++\n\tp.lookup[id] = ptr\n\tp.Unlock()\n\treturn C.uint(id)\n}\n\n// Deref takes an id and returns the corresponding pointer if it exists.\nfunc (p *ptrProxy) Deref(id C.uint) (unsafe.Pointer, bool) {\n\tp.Lock()\n\tval, ok := p.lookup[uint(id)]\n\tp.Unlock()\n\treturn val, ok\n}\n\n// Free releases a registered pointer by its id.\nfunc (p *ptrProxy) Free(id C.uint) {\n\tp.Lock()\n\tdelete(p.lookup, uint(id))\n\tp.Unlock()\n}\n"
  },
  {
    "path": "responsewriter.go",
    "content": "package main\n\n// #include <stdio.h>\nimport \"C\"\nimport (\n\t\"net/http\"\n\t\"unsafe\"\n)\n\n//export ResponseWriter_Write\nfunc ResponseWriter_Write(wPtr C.uint, cbuf *C.char, length C.int) C.int {\n\tbuf := C.GoBytes(unsafe.Pointer(cbuf), length)\n\n\tw, ok := cpointers.Deref(wPtr)\n\tif !ok {\n\t\treturn C.EOF\n\t}\n\n\tn, err := (*(*http.ResponseWriter)(w)).Write(buf)\n\tif err != nil {\n\t\treturn C.EOF\n\t}\n\treturn C.int(n)\n}\n\n//export ResponseWriter_WriteHeader\nfunc ResponseWriter_WriteHeader(wPtr C.uint, header C.int) {\n\tw, ok := cpointers.Deref(wPtr)\n\tif !ok {\n\t\treturn\n\t}\n\t(*(*http.ResponseWriter)(w)).WriteHeader(int(header))\n}\n"
  }
]