Add SetTimeout/SetInterval to JailCell otto.Otto, closes #189

* Commit initial change for settimeout/setinterval

* Add initial tests for jail setTimeout/setInterval

* Add ottoext dependency

* Add fetch jail test with function

* Add dependencies of fetch from ottoext

* Refactor with regards to PR review

* Refactor with regards to PR review

* Fix syntax errors

* Fix missing return statement
This commit is contained in:
Ewetumo Alexander 2017-07-13 12:04:47 +01:00 committed by Ivan Tomilov
parent fb7738b1bb
commit b50c46caa8
76 changed files with 6026 additions and 5 deletions

View File

@ -16,6 +16,8 @@ import (
"github.com/robertkrimen/otto"
"github.com/status-im/status-go/geth/params"
"github.com/status-im/status-go/static"
"fknsrs.biz/p/ottoext/loop"
)
// errors
@ -171,9 +173,28 @@ type TxQueueManager interface {
DiscardTransactions(ids string) map[string]RawDiscardTransactionResult
}
// JailExecutor defines an interface which exposes method to be executed
// against a Jail vm.
type JailExecutor interface {
// Run exist so we are able to execute js code on pure otto.VM without runing
// it on the event loop.
Run(string) (otto.Value, error)
// Exec exists for the purpose to execute has normal on the event loop provided by
// ottoext.
Exec(string) (otto.Value, error)
// Fetch calls the underlying FetchAPI which makes http request
// to desired path. (See https://developer.mozilla.org/en/docs/Web/API/Fetch_API).
Fetch(string, func(otto.Value)) (otto.Value, error)
}
// JailCell represents single jail cell, which is basically a JavaScript VM.
type JailCell interface {
CellVM() *otto.Otto
CellLoop() *loop.Loop
Executor() JailExecutor
Copy() (JailCell, error)
}
// JailManager defines methods for managing jailed environments

View File

@ -13,6 +13,10 @@ import (
"github.com/robertkrimen/otto"
"github.com/status-im/status-go/geth/common"
"github.com/status-im/status-go/static"
"fknsrs.biz/p/ottoext/fetch"
"fknsrs.biz/p/ottoext/loop"
"fknsrs.biz/p/ottoext/timers"
)
const (
@ -31,9 +35,32 @@ var (
type JailCell struct {
id string
vm *otto.Otto
lo *loop.Loop
sem *semaphore.Semaphore
}
// newJailCell encapsulates what we need to create a new jailCell from the
// provided vm and eventloop instance.
func newJailCell(id string, vm *otto.Otto, lo *loop.Loop) (*JailCell, error) {
// Register fetch provider from ottoext.
if err := fetch.Define(vm, lo); err != nil {
return nil, err
}
// Register event loop for timers.
if err := timers.Define(vm, lo); err != nil {
return nil, err
}
return &JailCell{
id: id,
vm: vm,
lo: lo,
sem: semaphore.New(1, JailCellRequestTimeout*time.Second),
}, nil
}
// Jail represents jailed environment inside of which we hold multiple cells.
// Each cell is a separate JavaScript VM.
type Jail struct {
@ -43,6 +70,60 @@ type Jail struct {
baseJSCode string // JavaScript used to initialize all new cells with
}
// Copy returns a new JailCell instance with a new eventloop runtime associated with
// the given cell.
func (cell *JailCell) Copy() (common.JailCell, error) {
vmCopy := cell.vm.Copy()
return newJailCell(cell.id, vmCopy, loop.New(vmCopy))
}
// Fetch attempts to call the underline Fetch API added through the
// ottoext package.
func (cell *JailCell) Fetch(url string, callback func(otto.Value)) (otto.Value, error) {
if err := cell.vm.Set("__captureFetch", callback); err != nil {
return otto.UndefinedValue(), err
}
return cell.Exec(`fetch("` + url + `").then(function(response){
__captureFetch({
"url": response.url,
"type": response.type,
"body": response.text(),
"status": response.status,
"headers": response.headers,
});
});
`)
}
// Exec evaluates the giving js string on the associated vm loop returning
// an error.
func (cell *JailCell) Exec(val string) (otto.Value, error) {
res, err := cell.vm.Run(val)
if err != nil {
return res, err
}
return res, cell.lo.Run()
}
// Run evaluates the giving js string on the associated vm llop.
func (cell *JailCell) Run(val string) (otto.Value, error) {
return cell.vm.Run(val)
}
// CellLoop returns the ottoext.Loop instance which provides underline timeout/setInternval
// event runtime for the Jail vm.
func (cell *JailCell) CellLoop() *loop.Loop {
return cell.lo
}
// Executor returns a structure which implements the common.JailExecutor.
func (cell *JailCell) Executor() common.JailExecutor {
return cell
}
// CellVM returns the associated otto.Vm connect to the giving cell.
func (cell *JailCell) CellVM() *otto.Otto {
return cell.vm
}
@ -62,11 +143,16 @@ func (jail *Jail) BaseJS(js string) {
// NewJailCell initializes and returns jail cell
func (jail *Jail) NewJailCell(id string) common.JailCell {
return &JailCell{
id: id,
vm: otto.New(),
sem: semaphore.New(1, JailCellRequestTimeout*time.Second),
vm := otto.New()
newJail, err := newJailCell(id, vm, loop.New(vm))
if err != nil {
//TODO(alex): Should we really panic here, his there
// a better way. Think on it.
panic(err)
}
return newJail
}
// Parse creates a new jail cell context, with the given chatID as identifier.
@ -127,7 +213,15 @@ func (jail *Jail) Call(chatID string, path string, args string) string {
}
jail.RUnlock()
vm := cell.CellVM().Copy() // isolate VM to allow concurrent access
// Due to the new timer assigned we need to clone existing cell to allow
// unique cell runtime and eventloop context.
cellCopy, err := cell.Copy()
if err != nil {
return makeError(err.Error())
}
// isolate VM to allow concurrent access
vm := cellCopy.CellVM()
res, err := vm.Call("call", nil, path, args)
return makeResult(res.String(), err)

View File

@ -3,9 +3,12 @@ package jail_test
import (
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/robertkrimen/otto"
"github.com/status-im/status-go/geth/jail"
"github.com/status-im/status-go/geth/node"
"github.com/status-im/status-go/geth/params"
@ -115,6 +118,83 @@ func (s *JailTestSuite) TestFunctionCall() {
require.Equal(expectedResponse, response)
}
func (s *JailTestSuite) TestJailTimeoutFailure() {
require := s.Require()
require.NotNil(s.jail)
newCell := s.jail.NewJailCell(testChatID)
require.NotNil(newCell)
execr := newCell.Executor()
// Attempt to run a timeout string against a JailCell.
_, err := execr.Exec(`
setTimeout(function(n){
if(Date.now() - n < 50){
throw new Error("Timedout early");
}
return n;
}, 30, Date.now());
`)
require.NotNil(err)
}
func (s *JailTestSuite) TestJailTimeout() {
require := s.Require()
require.NotNil(s.jail)
newCell := s.jail.NewJailCell(testChatID)
require.NotNil(newCell)
execr := newCell.Executor()
// Attempt to run a timeout string against a JailCell.
res, err := execr.Exec(`
setTimeout(function(n){
if(Date.now() - n < 50){
throw new Error("Timedout early");
}
return n;
}, 50, Date.now());
`)
require.NoError(err)
require.NotNil(res)
}
func (s *JailTestSuite) TestJailFetch() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("Hello World"))
})
server := httptest.NewServer(mux)
defer server.Close()
require := s.Require()
require.NotNil(s.jail)
newCell := s.jail.NewJailCell(testChatID)
require.NotNil(newCell)
execr := newCell.Executor()
wait := make(chan struct{})
// Attempt to run a fetch resource.
_, err := execr.Fetch(server.URL, func(res otto.Value) {
go func() { wait <- struct{}{} }()
})
require.NoError(err)
<-wait
}
func (s *JailTestSuite) TestJailRPCSend() {
require := s.Require()
require.NotNil(s.jail)

26
vendor/fknsrs.biz/p/ottoext/LICENSE.md vendored Normal file
View File

@ -0,0 +1,26 @@
Copyright (c) 2015, Deoxxa Development
======================================
All rights reserved.
--------------------
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of Deoxxa Development nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY DEOXXA DEVELOPMENT ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL DEOXXA DEVELOPMENT BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/fknsrs.biz/p/ottoext/README.md vendored Normal file
View File

@ -0,0 +1,22 @@
ottoext
=======
[![GoDoc](https://godoc.org/fknsrs.biz/p/ottoext?status.svg)](https://godoc.org/fknsrs.biz/p/ottoext)
Overview
--------
This package contains some extensions for the otto JavaScript interpreter. The
most important extension is a generic event loop based on code from natto. The
other extensions are `setTimeout` and `setInterval` support, `Promise` support
(via `native-promise-only`, MIT license), and `fetch` support.
Examples
--------
Take a look at the test files to see how the extensions work.
License
-------
3-clause BSD. A copy is included with the source.

View File

@ -0,0 +1 @@
/ottoext

View File

@ -0,0 +1,12 @@
var x = fetch('http://www.example.com/').then(function(r) {
r.text().then(function(d) {
console.log(r.statusText);
for (var k in r.headers._headers) {
console.log(k + ':', r.headers.get(k));
}
console.log('');
console.log(d);
});
});

View File

@ -0,0 +1,90 @@
// command otto runs JavaScript from a file, opens a repl, or does both.
package main
import (
"flag"
"io"
"io/ioutil"
"fknsrs.biz/p/ottoext/loop"
"fknsrs.biz/p/ottoext/loop/looptask"
erepl "fknsrs.biz/p/ottoext/repl"
"github.com/robertkrimen/otto"
"github.com/robertkrimen/otto/repl"
"fknsrs.biz/p/ottoext/fetch"
"fknsrs.biz/p/ottoext/process"
"fknsrs.biz/p/ottoext/promise"
"fknsrs.biz/p/ottoext/timers"
)
var (
openRepl = flag.Bool("repl", false, "Always open a REPL, even if a file is provided.")
debugger = flag.Bool("debugger", false, "Attach REPL-based debugger.")
)
func main() {
flag.Parse()
vm := otto.New()
if *debugger {
vm.SetDebuggerHandler(repl.DebuggerHandler)
}
l := loop.New(vm)
if err := timers.Define(vm, l); err != nil {
panic(err)
}
if err := promise.Define(vm, l); err != nil {
panic(err)
}
if err := fetch.Define(vm, l); err != nil {
panic(err)
}
if err := process.Define(vm, flag.Args()); err != nil {
panic(err)
}
blockingTask := looptask.NewEvalTask("")
if len(flag.Args()) == 0 || *openRepl {
l.Add(blockingTask)
}
if len(flag.Args()) > 0 {
d, err := ioutil.ReadFile(flag.Arg(0))
if err != nil {
panic(err)
}
// this is a very cheap way of "supporting" shebang lines
if d[0] == '#' {
d = []byte("// " + string(d))
}
s, err := vm.Compile(flag.Arg(0), string(d))
if err != nil {
panic(err)
}
if err := l.Eval(s); err != nil {
panic(err)
}
}
if len(flag.Args()) == 0 || *openRepl {
go func() {
if err := erepl.Run(l); err != nil && err != io.EOF {
panic(err)
}
l.Ready(blockingTask)
}()
}
if err := l.Run(); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,13 @@
dist-fetch.rice-box.go: dist-fetch/bundle.js
rice embed-go
dist-fetch/bundle.js: js/bundle.js
mkdir -p dist-fetch
cp -a js/bundle.js dist-fetch/bundle.js
cp -a js/bundle.js.map dist-fetch/bundle.js.map
js/bundle.js: js/fetch.js js/headers.js js/index.js js/request.js js/response.js
cd js && npm install && npm run bundle${BUNDLE_SUFFIX}
clean:
rm -rf dist-fetch.rice-box.go dist-fetch js/bundle.js

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,359 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/*!******************!*\
!*** ./index.js ***!
\******************/
/***/ function(module, exports, __webpack_require__) {
'use strict';
__webpack_require__(/*! expose?fetch!./fetch */ 1);
__webpack_require__(/*! expose?Headers!./headers */ 6);
__webpack_require__(/*! expose?Request!./request */ 7);
__webpack_require__(/*! expose?Response!./response */ 8);
/***/ },
/* 1 */
/*!******************************************!*\
!*** ./~/expose-loader?fetch!./fetch.js ***!
\******************************************/
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {module.exports = global["fetch"] = __webpack_require__(/*! -!./~/babel-loader?stage=0!./fetch.js */ 2);
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
/* 2 */
/*!*******************************************!*\
!*** ./~/babel-loader?stage=0!./fetch.js ***!
\*******************************************/
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
exports['default'] = fetch;
var Request = __webpack_require__(/*! ./request */ 3);
var Response = __webpack_require__(/*! ./response */ 5);
function fetch(input, init) {
var req = new Request(input, init);
var res = new Response();
return new Promise(function (resolve, reject) {
return __private__fetch_execute(req, res, function (err) {
if (err) {
return reject(err);
}
return resolve(res);
});
});
}
module.exports = exports['default'];
/***/ },
/* 3 */
/*!*********************************************!*\
!*** ./~/babel-loader?stage=0!./request.js ***!
\*********************************************/
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var Headers = __webpack_require__(/*! ./headers */ 4);
var Request = function Request(input) {
var _ref = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var method = _ref.method;
var headers = _ref.headers;
var redirect = _ref.redirect;
var body = _ref.body;
_classCallCheck(this, Request);
this.method = 'GET';
this.headers = new Headers({});
this.redirect = 'manual';
this.body = null;
if (input instanceof Request) {
this.url = input.url;
this.method = input.method;
this.headers = new Headers(input.headers);
this.redirect = input.redirect;
} else {
this.url = input;
}
if (method) {
this.method = method;
}
if (headers) {
this.headers = new Headers(headers);
}
if (redirect) {
this.redirect = redirect;
}
if (body) {
this.body = body;
}
};
exports['default'] = Request;
module.exports = exports['default'];
/***/ },
/* 4 */
/*!*********************************************!*\
!*** ./~/babel-loader?stage=0!./headers.js ***!
\*********************************************/
/***/ function(module, exports) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var Headers = (function () {
function Headers(init) {
var _this = this;
_classCallCheck(this, Headers);
this._headers = {};
if (init instanceof Headers) {
init = init._headers;
}
if (typeof init === 'object' && init !== null) {
for (var k in init) {
var v = init[k];
if (!Array.isArray(v)) {
v = [v];
}
v.forEach(function (e) {
return _this.append(k, e);
});
}
}
}
_createClass(Headers, [{
key: 'append',
value: function append(name, value) {
var normalisedName = Headers.normaliseName(name);
if (!Object.hasOwnProperty.call(this._headers, normalisedName)) {
this._headers[normalisedName] = [];
}
this._headers[normalisedName].push(value);
}
}, {
key: 'delete',
value: function _delete(name) {
delete this._headers[Headers.normaliseName(name)];
}
}, {
key: 'get',
value: function get(name) {
var normalisedName = Headers.normaliseName(name);
if (this._headers[normalisedName]) {
return this._headers[normalisedName][0];
}
}
}, {
key: 'getAll',
value: function getAll(name) {
return this._headers[Headers.normaliseName(name)] || [];
}
}, {
key: 'has',
value: function has(name) {
var normalisedName = Headers.normaliseName(name);
return Array.isArray(this._headers[normalisedName]);
}
}, {
key: 'set',
value: function set(name, value) {
var normalisedName = Headers.normaliseName(name);
this._headers[normalisedName] = [value];
}
}], [{
key: 'normaliseName',
value: function normaliseName(name) {
return name.toLowerCase();
}
}]);
return Headers;
})();
exports['default'] = Headers;
module.exports = exports['default'];
/***/ },
/* 5 */
/*!**********************************************!*\
!*** ./~/babel-loader?stage=0!./response.js ***!
\**********************************************/
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var Headers = __webpack_require__(/*! ./headers */ 4);
var Response = (function () {
function Response(body) {
var _ref = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var _ref$status = _ref.status;
var status = _ref$status === undefined ? 200 : _ref$status;
var _ref$statusText = _ref.statusText;
var statusText = _ref$statusText === undefined ? 'OK' : _ref$statusText;
var _ref$headers = _ref.headers;
var headers = _ref$headers === undefined ? {} : _ref$headers;
_classCallCheck(this, Response);
this.bodyUsed = true;
this._body = null;
this.headers = new Headers(headers);
this.ok = status >= 200 && status < 300;
this.status = status;
this.statusText = statusText;
this.type = this.headers.get('content-type');
}
_createClass(Response, [{
key: 'text',
value: function text() {
var _this = this;
return new Promise(function (resolve) {
return resolve(_this._body);
});
}
}, {
key: 'json',
value: function json() {
return this.text().then(function (d) {
return JSON.parse(d);
});
}
}]);
return Response;
})();
exports['default'] = Response;
module.exports = exports['default'];
/***/ },
/* 6 */
/*!**********************************************!*\
!*** ./~/expose-loader?Headers!./headers.js ***!
\**********************************************/
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {module.exports = global["Headers"] = __webpack_require__(/*! -!./~/babel-loader?stage=0!./headers.js */ 4);
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
/* 7 */
/*!**********************************************!*\
!*** ./~/expose-loader?Request!./request.js ***!
\**********************************************/
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {module.exports = global["Request"] = __webpack_require__(/*! -!./~/babel-loader?stage=0!./request.js */ 3);
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
/* 8 */
/*!************************************************!*\
!*** ./~/expose-loader?Response!./response.js ***!
\************************************************/
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {module.exports = global["Response"] = __webpack_require__(/*! -!./~/babel-loader?stage=0!./response.js */ 5);
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ }
/******/ ]);
//# sourceMappingURL=bundle.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,157 @@
package fetch // import "fknsrs.biz/p/ottoext/fetch"
import (
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"github.com/GeertJohan/go.rice"
"github.com/robertkrimen/otto"
"fknsrs.biz/p/ottoext/loop"
"fknsrs.biz/p/ottoext/promise"
)
func mustValue(v otto.Value, err error) otto.Value {
if err != nil {
panic(err)
}
return v
}
type fetchTask struct {
id int64
jsReq, jsRes *otto.Object
cb otto.Value
err error
status int
statusText string
headers map[string][]string
body []byte
}
func (t *fetchTask) SetID(id int64) { t.id = id }
func (t *fetchTask) GetID() int64 { return t.id }
func (t *fetchTask) Execute(vm *otto.Otto, l *loop.Loop) error {
var arguments []interface{}
if t.err != nil {
e, err := vm.Call(`new Error`, nil, t.err.Error())
if err != nil {
return err
}
arguments = append(arguments, e)
}
t.jsRes.Set("status", t.status)
t.jsRes.Set("statusText", t.statusText)
h := mustValue(t.jsRes.Get("headers")).Object()
for k, vs := range t.headers {
for _, v := range vs {
if _, err := h.Call("append", k, v); err != nil {
return err
}
}
}
t.jsRes.Set("_body", string(t.body))
if _, err := t.cb.Call(otto.NullValue(), arguments...); err != nil {
return err
}
return nil
}
func (t *fetchTask) Cancel() {
}
func Define(vm *otto.Otto, l *loop.Loop) error {
return DefineWithHandler(vm, l, nil)
}
func DefineWithHandler(vm *otto.Otto, l *loop.Loop, h http.Handler) error {
if err := promise.Define(vm, l); err != nil {
return err
}
jsData := rice.MustFindBox("dist-fetch").MustString("bundle.js")
smData := rice.MustFindBox("dist-fetch").MustString("bundle.js.map")
s, err := vm.CompileWithSourceMap("fetch-bundle.js", jsData, smData)
if err != nil {
return err
}
if _, err := vm.Run(s); err != nil {
return err
}
vm.Set("__private__fetch_execute", func(c otto.FunctionCall) otto.Value {
jsReq := c.Argument(0).Object()
jsRes := c.Argument(1).Object()
cb := c.Argument(2)
method := mustValue(jsReq.Get("method")).String()
urlStr := mustValue(jsReq.Get("url")).String()
jsBody := mustValue(jsReq.Get("body"))
var body io.Reader
if jsBody.IsString() {
body = strings.NewReader(jsBody.String())
}
t := &fetchTask{
jsReq: jsReq,
jsRes: jsRes,
cb: cb,
}
l.Add(t)
go func() {
defer l.Ready(t)
req, err := http.NewRequest(method, urlStr, body)
if err != nil {
t.err = err
return
}
if h != nil && urlStr[0] == '/' {
res := httptest.NewRecorder()
h.ServeHTTP(res, req)
t.status = res.Code
t.statusText = http.StatusText(res.Code)
t.headers = res.Header()
t.body = res.Body.Bytes()
} else {
res, err := http.DefaultClient.Do(req)
if err != nil {
t.err = err
return
}
d, err := ioutil.ReadAll(res.Body)
if err != nil {
t.err = err
return
}
t.status = res.StatusCode
t.statusText = res.Status
t.headers = res.Header
t.body = d
}
}()
return otto.UndefinedValue()
})
return nil
}

View File

@ -0,0 +1 @@
/node_modules

View File

@ -0,0 +1,359 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/*!******************!*\
!*** ./index.js ***!
\******************/
/***/ function(module, exports, __webpack_require__) {
'use strict';
__webpack_require__(/*! expose?fetch!./fetch */ 1);
__webpack_require__(/*! expose?Headers!./headers */ 6);
__webpack_require__(/*! expose?Request!./request */ 7);
__webpack_require__(/*! expose?Response!./response */ 8);
/***/ },
/* 1 */
/*!******************************************!*\
!*** ./~/expose-loader?fetch!./fetch.js ***!
\******************************************/
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {module.exports = global["fetch"] = __webpack_require__(/*! -!./~/babel-loader?stage=0!./fetch.js */ 2);
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
/* 2 */
/*!*******************************************!*\
!*** ./~/babel-loader?stage=0!./fetch.js ***!
\*******************************************/
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
exports['default'] = fetch;
var Request = __webpack_require__(/*! ./request */ 3);
var Response = __webpack_require__(/*! ./response */ 5);
function fetch(input, init) {
var req = new Request(input, init);
var res = new Response();
return new Promise(function (resolve, reject) {
return __private__fetch_execute(req, res, function (err) {
if (err) {
return reject(err);
}
return resolve(res);
});
});
}
module.exports = exports['default'];
/***/ },
/* 3 */
/*!*********************************************!*\
!*** ./~/babel-loader?stage=0!./request.js ***!
\*********************************************/
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var Headers = __webpack_require__(/*! ./headers */ 4);
var Request = function Request(input) {
var _ref = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var method = _ref.method;
var headers = _ref.headers;
var redirect = _ref.redirect;
var body = _ref.body;
_classCallCheck(this, Request);
this.method = 'GET';
this.headers = new Headers({});
this.redirect = 'manual';
this.body = null;
if (input instanceof Request) {
this.url = input.url;
this.method = input.method;
this.headers = new Headers(input.headers);
this.redirect = input.redirect;
} else {
this.url = input;
}
if (method) {
this.method = method;
}
if (headers) {
this.headers = new Headers(headers);
}
if (redirect) {
this.redirect = redirect;
}
if (body) {
this.body = body;
}
};
exports['default'] = Request;
module.exports = exports['default'];
/***/ },
/* 4 */
/*!*********************************************!*\
!*** ./~/babel-loader?stage=0!./headers.js ***!
\*********************************************/
/***/ function(module, exports) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var Headers = (function () {
function Headers(init) {
var _this = this;
_classCallCheck(this, Headers);
this._headers = {};
if (init instanceof Headers) {
init = init._headers;
}
if (typeof init === 'object' && init !== null) {
for (var k in init) {
var v = init[k];
if (!Array.isArray(v)) {
v = [v];
}
v.forEach(function (e) {
return _this.append(k, e);
});
}
}
}
_createClass(Headers, [{
key: 'append',
value: function append(name, value) {
var normalisedName = Headers.normaliseName(name);
if (!Object.hasOwnProperty.call(this._headers, normalisedName)) {
this._headers[normalisedName] = [];
}
this._headers[normalisedName].push(value);
}
}, {
key: 'delete',
value: function _delete(name) {
delete this._headers[Headers.normaliseName(name)];
}
}, {
key: 'get',
value: function get(name) {
var normalisedName = Headers.normaliseName(name);
if (this._headers[normalisedName]) {
return this._headers[normalisedName][0];
}
}
}, {
key: 'getAll',
value: function getAll(name) {
return this._headers[Headers.normaliseName(name)] || [];
}
}, {
key: 'has',
value: function has(name) {
var normalisedName = Headers.normaliseName(name);
return Array.isArray(this._headers[normalisedName]);
}
}, {
key: 'set',
value: function set(name, value) {
var normalisedName = Headers.normaliseName(name);
this._headers[normalisedName] = [value];
}
}], [{
key: 'normaliseName',
value: function normaliseName(name) {
return name.toLowerCase();
}
}]);
return Headers;
})();
exports['default'] = Headers;
module.exports = exports['default'];
/***/ },
/* 5 */
/*!**********************************************!*\
!*** ./~/babel-loader?stage=0!./response.js ***!
\**********************************************/
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var Headers = __webpack_require__(/*! ./headers */ 4);
var Response = (function () {
function Response(body) {
var _ref = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var _ref$status = _ref.status;
var status = _ref$status === undefined ? 200 : _ref$status;
var _ref$statusText = _ref.statusText;
var statusText = _ref$statusText === undefined ? 'OK' : _ref$statusText;
var _ref$headers = _ref.headers;
var headers = _ref$headers === undefined ? {} : _ref$headers;
_classCallCheck(this, Response);
this.bodyUsed = true;
this._body = null;
this.headers = new Headers(headers);
this.ok = status >= 200 && status < 300;
this.status = status;
this.statusText = statusText;
this.type = this.headers.get('content-type');
}
_createClass(Response, [{
key: 'text',
value: function text() {
var _this = this;
return new Promise(function (resolve) {
return resolve(_this._body);
});
}
}, {
key: 'json',
value: function json() {
return this.text().then(function (d) {
return JSON.parse(d);
});
}
}]);
return Response;
})();
exports['default'] = Response;
module.exports = exports['default'];
/***/ },
/* 6 */
/*!**********************************************!*\
!*** ./~/expose-loader?Headers!./headers.js ***!
\**********************************************/
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {module.exports = global["Headers"] = __webpack_require__(/*! -!./~/babel-loader?stage=0!./headers.js */ 4);
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
/* 7 */
/*!**********************************************!*\
!*** ./~/expose-loader?Request!./request.js ***!
\**********************************************/
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {module.exports = global["Request"] = __webpack_require__(/*! -!./~/babel-loader?stage=0!./request.js */ 3);
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
/* 8 */
/*!************************************************!*\
!*** ./~/expose-loader?Response!./response.js ***!
\************************************************/
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {module.exports = global["Response"] = __webpack_require__(/*! -!./~/babel-loader?stage=0!./response.js */ 5);
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ }
/******/ ]);
//# sourceMappingURL=bundle.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,17 @@
const Request = require('./request');
const Response = require('./response');
export default function fetch(input, init) {
const req = new Request(input, init);
const res = new Response();
return new Promise((resolve, reject) => {
return __private__fetch_execute(req, res, err => {
if (err) {
return reject(err);
}
return resolve(res);
});
});
}

View File

@ -0,0 +1,62 @@
export default class Headers {
_headers = {};
constructor(init) {
if (init instanceof Headers) {
init = init._headers;
}
if (typeof init === 'object' && init !== null) {
for (var k in init) {
var v = init[k];
if (!Array.isArray(v)) {
v = [v];
}
v.forEach(e => this.append(k, e));
}
}
}
append(name, value) {
const normalisedName = Headers.normaliseName(name);
if (!Object.hasOwnProperty.call(this._headers, normalisedName)) {
this._headers[normalisedName] = [];
}
this._headers[normalisedName].push(value);
}
delete(name) {
delete this._headers[Headers.normaliseName(name)];
}
get(name) {
const normalisedName = Headers.normaliseName(name);
if (this._headers[normalisedName]) {
return this._headers[normalisedName][0];
}
}
getAll(name) {
return this._headers[Headers.normaliseName(name)] || [];
}
has(name) {
const normalisedName = Headers.normaliseName(name);
return Array.isArray(this._headers[normalisedName]);
}
set(name, value) {
const normalisedName = Headers.normaliseName(name);
this._headers[normalisedName] = [value];
}
static normaliseName(name) {
return name.toLowerCase();
}
}

View File

@ -0,0 +1,4 @@
require('expose?fetch!./fetch');
require('expose?Headers!./headers');
require('expose?Request!./request');
require('expose?Response!./response');

View File

@ -0,0 +1,13 @@
{
"scripts": {
"bundle": "webpack -v -d",
"watch": "webpack -v -w"
},
"devDependencies": {
"babel-core": "^5.8.25",
"babel-loader": "^5.3.2",
"babel-runtime": "^5.8.25",
"expose-loader": "^0.7.0",
"webpack": "^1.12.2"
}
}

View File

@ -0,0 +1,35 @@
const Headers = require('./headers');
export default class Request {
constructor(input, {method, headers, redirect, body}={}) {
this.method = 'GET';
this.headers = new Headers({});
this.redirect = 'manual';
this.body = null;
if (input instanceof Request) {
this.url = input.url;
this.method = input.method;
this.headers = new Headers(input.headers);
this.redirect = input.redirect;
} else {
this.url = input;
}
if (method) {
this.method = method;
}
if (headers) {
this.headers = new Headers(headers);
}
if (redirect) {
this.redirect = redirect;
}
if (body) {
this.body = body;
}
}
}

View File

@ -0,0 +1,23 @@
const Headers = require('./headers');
export default class Response {
bodyUsed = true;
_body = null;
constructor(body, {status=200, statusText='OK', headers={}}={}) {
this.headers = new Headers(headers);
this.ok = status >= 200 && status < 300;
this.status = status;
this.statusText = statusText;
this.type = this.headers.get('content-type');
}
text() {
return new Promise(resolve => resolve(this._body));
}
json() {
return this.text().then(d => JSON.parse(d));
}
}

View File

@ -0,0 +1,23 @@
module.exports = {
devtool: 'source-map',
entry: './index',
output: {
filename: 'bundle.js',
},
resolve: {
extensions: ['', '.js'],
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel?stage=0',
},
],
},
node: {
console: false,
process: false,
setImmediate: false,
},
};

189
vendor/fknsrs.biz/p/ottoext/loop/loop.go vendored Normal file
View File

@ -0,0 +1,189 @@
package loop // import "fknsrs.biz/p/ottoext/loop"
import (
"fmt"
"sync"
"sync/atomic"
"github.com/robertkrimen/otto"
)
func formatTask(t Task) string {
if t == nil {
return "<nil>"
}
return fmt.Sprintf("<%T> %d", t, t.GetID())
}
// Task represents something that the event loop can schedule and run.
//
// Task describes two operations that will almost always be boilerplate,
// SetID and GetID. They exist so that the event loop can identify tasks
// after they're added.
//
// Execute is called when a task has been pulled from the "ready" queue.
//
// Cancel is called when a task is removed from the loop without being
// finalised.
type Task interface {
SetID(id int64)
GetID() int64
Execute(vm *otto.Otto, l *Loop) error
Cancel()
}
// Loop encapsulates the event loop's state. This includes the vm on which the
// loop operates, a monotonically incrementing event id, a map of tasks that
// aren't ready yet, keyed by their ID, and a channel of tasks that are ready
// to finalise on the VM. The channel holding the tasks pending finalising can
// be buffered or unbuffered.
type Loop struct {
vm *otto.Otto
id int64
lock sync.RWMutex
tasks map[int64]Task
ready chan Task
closed bool
}
// New creates a new Loop with an unbuffered ready queue on a specific VM.
func New(vm *otto.Otto) *Loop {
return NewWithBacklog(vm, 0)
}
// NewWithBacklog creates a new Loop on a specific VM, giving it a buffered
// queue, the capacity of which being specified by the backlog argument.
func NewWithBacklog(vm *otto.Otto, backlog int) *Loop {
return &Loop{
vm: vm,
tasks: make(map[int64]Task),
ready: make(chan Task, backlog),
}
}
// VM gets the JavaScript interpreter associated with the loop. This will be
// some kind of Otto object, but it's wrapped in an interface so the
// `ottoext` library can work with forks/extensions of otto.
func (l *Loop) VM() *otto.Otto {
return l.vm
}
// Add puts a task into the loop. This signals to the loop that this task is
// doing something outside of the JavaScript environment, and that at some
// point, it will become ready for finalising.
func (l *Loop) Add(t Task) {
l.lock.Lock()
t.SetID(atomic.AddInt64(&l.id, 1))
l.tasks[t.GetID()] = t
l.lock.Unlock()
}
// Remove takes a task out of the loop. This should not be called if a task
// has already become ready for finalising. Warranty void if constraint is
// broken.
func (l *Loop) Remove(t Task) {
l.remove(t)
go l.Ready(nil)
}
func (l *Loop) remove(t Task) {
l.removeByID(t.GetID())
}
func (l *Loop) removeByID(id int64) {
l.lock.Lock()
delete(l.tasks, id)
l.lock.Unlock()
}
// Ready signals to the loop that a task is ready to be finalised. This might
// block if the "ready channel" in the loop is at capacity.
func (l *Loop) Ready(t Task) {
if l.closed {
return
}
l.ready <- t
}
// EvalAndRun is a combination of Eval and Run. Creatively named.
func (l *Loop) EvalAndRun(s interface{}) error {
if err := l.Eval(s); err != nil {
return err
}
return l.Run()
}
// Eval executes some code in the VM associated with the loop and returns an
// error if that execution fails.
func (l *Loop) Eval(s interface{}) error {
if _, err := l.vm.Run(s); err != nil {
return err
}
return nil
}
func (l *Loop) processTask(t Task) error {
id := t.GetID()
if err := t.Execute(l.vm, l); err != nil {
l.lock.RLock()
for _, t := range l.tasks {
t.Cancel()
}
l.lock.RUnlock()
return err
}
l.removeByID(id)
return nil
}
// Run handles the task scheduling and finalisation. It will block until
// there's no work left to do, or an error occurs.
func (l *Loop) Run() error {
for {
l.lock.Lock()
if len(l.tasks) == 0 {
// prevent any more tasks entering the ready channel
l.closed = true
l.lock.Unlock()
break
}
l.lock.Unlock()
t := <-l.ready
if t != nil {
if err := l.processTask(t); err != nil {
return err
}
}
}
// drain ready channel of any existing tasks
outer:
for {
select {
case t := <-l.ready:
if t != nil {
if err := l.processTask(t); err != nil {
return err
}
}
default:
break outer
}
}
close(l.ready)
return nil
}

View File

@ -0,0 +1,126 @@
package looptask
import (
"errors"
"fknsrs.biz/p/ottoext/loop"
"github.com/robertkrimen/otto"
)
// IdleTask is designed to sit in a loop and keep it active, without doing any
// work.
type IdleTask struct {
ID int64
}
// NewIdleTask creates a new IdleTask object.
func NewIdleTask() *IdleTask {
return &IdleTask{}
}
// SetID sets the ID of an IdleTask.
func (i *IdleTask) SetID(ID int64) { i.ID = ID }
// GetID gets the ID of an IdleTask.
func (i IdleTask) GetID() int64 { return i.ID }
// Cancel does nothing on an IdleTask, as there's nothing to clean up.
func (i IdleTask) Cancel() {}
// Execute always returns an error for an IdleTask, as it should never
// actually be run.
func (i IdleTask) Execute(vm *otto.Otto, l *loop.Loop) error {
return errors.New("Idle task should never execute")
}
// EvalTask schedules running an otto.Script. It has two channels for
// communicating the result of the execution.
type EvalTask struct {
ID int64
Script interface{}
Value chan otto.Value
Error chan error
SoftError bool
}
// NewEvalTask creates a new EvalTask for a given otto.Script, creating two
// buffered channels for the response.
func NewEvalTask(s interface{}) *EvalTask {
return &EvalTask{
Script: s,
Value: make(chan otto.Value, 1),
Error: make(chan error, 1),
}
}
// SetID sets the ID of an EvalTask.
func (e *EvalTask) SetID(ID int64) { e.ID = ID }
// GetID gets the ID of an EvalTask.
func (e EvalTask) GetID() int64 { return e.ID }
// Cancel does nothing for an EvalTask, as there's nothing to clean up.
func (e EvalTask) Cancel() {}
// Execute runs the EvalTask's otto.Script in the vm provided, pushing the
// resultant return value and error (or nil) into the associated channels.
// If the execution results in an error, it will return that error.
func (e EvalTask) Execute(vm *otto.Otto, l *loop.Loop) error {
v, err := vm.Run(e.Script)
e.Value <- v
e.Error <- err
if e.SoftError {
return nil
}
return err
}
// CallTask schedules an otto.Value (which should be a function) to be called
// with a specific set of arguments. It has two channels for communicating the
// result of the call.
type CallTask struct {
ID int64
Function otto.Value
Args []interface{}
Value chan otto.Value
Error chan error
SoftError bool
}
// NewCallTask creates a new CallTask object for a given otto.Value (which
// should be a function) and set of arguments, creating two buffered channels
// for the response.
func NewCallTask(fn otto.Value, args ...interface{}) *CallTask {
return &CallTask{
Function: fn,
Args: args,
Value: make(chan otto.Value, 1),
Error: make(chan error, 1),
}
}
// SetID sets the ID of a CallTask.
func (c *CallTask) SetID(ID int64) { c.ID = ID }
// GetID gets the ID of a CallTask.
func (c CallTask) GetID() int64 { return c.ID }
// Cancel does nothing for a CallTask, as there's nothing to clean up.
func (c CallTask) Cancel() {}
// Execute calls the associated function (not necessarily in the given vm),
// pushing the resultant return value and error (or nil) into the associated
// channels. If the call results in an error, it will return that error.
func (c CallTask) Execute(vm *otto.Otto, l *loop.Loop) error {
v, err := c.Function.Call(otto.NullValue(), c.Args...)
c.Value <- v
c.Error <- err
if c.SoftError {
return nil
}
return err
}

2
vendor/fknsrs.biz/p/ottoext/main.go vendored Normal file
View File

@ -0,0 +1,2 @@
// Package ottoext contains some extensions for the otto JavaScript interpreter.
package ottoext // import "fknsrs.biz/p/ottoext"

View File

@ -0,0 +1,27 @@
package process // import "fknsrs.biz/p/ottoext/process"
import (
"os"
"strings"
"github.com/robertkrimen/otto"
)
func Define(vm *otto.Otto, argv []string) error {
if v, err := vm.Get("process"); err != nil {
return err
} else if !v.IsUndefined() {
return nil
}
env := make(map[string]string)
for _, e := range os.Environ() {
a := strings.SplitN(e, "=", 2)
env[a[0]] = a[1]
}
return vm.Set("process", map[string]interface{}{
"env": env,
"argv": argv,
})
}

View File

@ -0,0 +1,5 @@
dist-promise.rice-box.go: dist-promise/bundle.js
rice embed-go
clean:
rm -rf dist-promise.rice-box.go

View File

@ -0,0 +1,315 @@
'use strict';
/**
* @constructor
*/
function Promise(resolver) {
if (typeof resolver !== 'function' && typeof resolver !== 'undefined') {
throw new TypeError();
}
if (typeof this !== 'object' || (this && this.then)) {
throw new TypeError();
}
var self = this;
// states
// 0: pending
// 1: resolving
// 2: rejecting
// 3: resolved
// 4: rejected
var state = 0;
var val = 0;
var next = [];
var fn = null;
var er = null;
this.promise = this;
this.resolve = function resolve(v) {
fn = self.fn;
er = self.er;
if (!state) {
val = v;
state = 1;
setImmediate(fire);
}
return self;
};
this.reject = function reject(v) {
fn = self.fn;
er = self.er;
if (!state) {
val = v;
state = 2;
setImmediate(fire);
}
return self;
};
this._p = 1;
this.then = function then(_fn, _er) {
if (!(this._p === 1)) {
throw new TypeError();
}
var p = new Promise();
p.fn = _fn;
p.er = _er;
switch (state) {
case 3:
p.resolve(val);
break;
case 4:
p.reject(val);
break;
default:
next.push(p);
break;
}
return p;
};
this.catch = function _catch(_er) {
return self.then(null, _er);
};
var finish = function finish(type) {
state = type || 4;
next.map(function(p) {
state === 3 && p.resolve(val) || p.reject(val);
});
};
try {
if (typeof resolver === 'function') {
resolver(this.resolve, this.reject);
}
} catch (e) {
this.reject(e);
}
return this;
// ref: reference to 'then' function
// cb, ec, cn: successCallback, failureCallback, notThennableCallback
function thennable (ref, cb, ec, cn) {
if ((typeof val === 'object' || typeof val === 'function') && typeof ref === 'function') {
try {
// cnt protects against abuse calls from spec checker
var cnt = 0;
ref.call(val, function(v) {
if (cnt++) {
return;
}
val = v;
cb();
}, function(v) {
if (cnt++) {
return;
}
val = v;
ec();
})
} catch (e) {
val = e;
ec();
}
} else {
cn();
}
}
function fire() {
// check if it's a thenable
var ref;
try {
ref = val && val.then;
} catch (e) {
val = e;
state = 2;
return fire();
}
thennable(ref, function() {
state = 1;
fire();
}, function() {
state = 2;
fire();
}, function() {
try {
if (state === 1 && typeof fn === 'function') {
val = fn(val);
} else if (state === 2 && typeof er === 'function') {
val = er(val);
state = 1;
}
} catch (e) {
val = e;
return finish();
}
if (val === self) {
val = TypeError();
finish();
} else {
thennable(ref, function() {
finish(3);
}, finish, function() {
finish(state === 1 && 3);
});
}
});
}
}
Promise.resolve = function resolve(value) {
if (!(this._p === 1)) {
throw new TypeError();
}
if (value instanceof Promise) {
return value;
}
return new Promise(function(resolve) {
resolve(value);
});
};
Promise.reject = function reject(value) {
if (!(this._p === 1)) {
throw new TypeError();
}
return new Promise(function(resolve, reject) {
reject(value);
});
};
Promise.all = function all(arr) {
if (!(this._p === 1)) {
throw new TypeError();
}
if (!(arr instanceof Array)) {
return Promise.reject(TypeError());
}
var p = new Promise();
function done(e, v) {
if (v) {
return p.resolve(v);
}
if (e) {
return p.reject(e);
}
var unresolved = arr.reduce(function(cnt, v) {
if (v && v.then) {
return cnt + 1;
}
return cnt;
}, 0);
if (unresolved === 0) {
p.resolve(arr);
}
arr.map(function(v, i) {
if (v && v.then) {
v.then(function(r) {
arr[i] = r;
done();
return r;
}, done);
}
});
}
done();
return d;
}
Promise.race = function race(arr) {
if (!(this._p === 1)) {
throw TypeError();
}
if (!(arr instanceof Array)) {
return Promise.reject(TypeError());
}
if (arr.length === 0) {
return new Promise();
}
var p = new Promise();
function done(e, v) {
if (v) {
return p.resolve(v);
}
if (e) {
return p.reject(e);
}
var unresolved = arr.reduce(function(cnt, v) {
if (v && v.then) {
return cnt + 1;
}
return cnt;
}, 0);
if (unresolved === 0) {
p.resolve(arr);
}
arr.map(function(v, i) {
if (v && v.then) {
v.then(function(r) {
done(null, r);
}, done);
}
});
}
done();
return p;
}
Promise._p = 1;

View File

@ -0,0 +1,318 @@
package promise
const src = `'use strict';
/**
* @constructor
*/
function Promise(resolver) {
if (typeof resolver !== 'function' && typeof resolver !== 'undefined') {
throw new TypeError();
}
if (typeof this !== 'object' || (this && this.then)) {
throw new TypeError();
}
var self = this;
// states
// 0: pending
// 1: resolving
// 2: rejecting
// 3: resolved
// 4: rejected
var state = 0;
var val = 0;
var next = [];
var fn = null;
var er = null;
this.promise = this;
this.resolve = function resolve(v) {
fn = self.fn;
er = self.er;
if (!state) {
val = v;
state = 1;
setImmediate(fire);
}
return self;
};
this.reject = function reject(v) {
fn = self.fn;
er = self.er;
if (!state) {
val = v;
state = 2;
setImmediate(fire);
}
return self;
};
this._p = 1;
this.then = function then(_fn, _er) {
if (!(this._p === 1)) {
throw new TypeError();
}
var p = new Promise();
p.fn = _fn;
p.er = _er;
switch (state) {
case 3:
p.resolve(val);
break;
case 4:
p.reject(val);
break;
default:
next.push(p);
break;
}
return p;
};
this.catch = function _catch(_er) {
return self.then(null, _er);
};
var finish = function finish(type) {
state = type || 4;
next.map(function(p) {
state === 3 && p.resolve(val) || p.reject(val);
});
};
try {
if (typeof resolver === 'function') {
resolver(this.resolve, this.reject);
}
} catch (e) {
this.reject(e);
}
return this;
// ref: reference to 'then' function
// cb, ec, cn: successCallback, failureCallback, notThennableCallback
function thennable (ref, cb, ec, cn) {
if ((typeof val === 'object' || typeof val === 'function') && typeof ref === 'function') {
try {
// cnt protects against abuse calls from spec checker
var cnt = 0;
ref.call(val, function(v) {
if (cnt++) {
return;
}
val = v;
cb();
}, function(v) {
if (cnt++) {
return;
}
val = v;
ec();
})
} catch (e) {
val = e;
ec();
}
} else {
cn();
}
}
function fire() {
// check if it's a thenable
var ref;
try {
ref = val && val.then;
} catch (e) {
val = e;
state = 2;
return fire();
}
thennable(ref, function() {
state = 1;
fire();
}, function() {
state = 2;
fire();
}, function() {
try {
if (state === 1 && typeof fn === 'function') {
val = fn(val);
} else if (state === 2 && typeof er === 'function') {
val = er(val);
state = 1;
}
} catch (e) {
val = e;
return finish();
}
if (val === self) {
val = TypeError();
finish();
} else {
thennable(ref, function() {
finish(3);
}, finish, function() {
finish(state === 1 && 3);
});
}
});
}
}
Promise.resolve = function resolve(value) {
if (!(this._p === 1)) {
throw new TypeError();
}
if (value instanceof Promise) {
return value;
}
return new Promise(function(resolve) {
resolve(value);
});
};
Promise.reject = function reject(value) {
if (!(this._p === 1)) {
throw new TypeError();
}
return new Promise(function(resolve, reject) {
reject(value);
});
};
Promise.all = function all(arr) {
if (!(this._p === 1)) {
throw new TypeError();
}
if (!(arr instanceof Array)) {
return Promise.reject(TypeError());
}
var p = new Promise();
function done(e, v) {
if (v) {
return p.resolve(v);
}
if (e) {
return p.reject(e);
}
var unresolved = arr.reduce(function(cnt, v) {
if (v && v.then) {
return cnt + 1;
}
return cnt;
}, 0);
if (unresolved === 0) {
p.resolve(arr);
}
arr.map(function(v, i) {
if (v && v.then) {
v.then(function(r) {
arr[i] = r;
done();
return r;
}, done);
}
});
}
done();
return p;
}
Promise.race = function race(arr) {
if (!(this._p === 1)) {
throw TypeError();
}
if (!(arr instanceof Array)) {
return Promise.reject(TypeError());
}
if (arr.length === 0) {
return new Promise();
}
var p = new Promise();
function done(e, v) {
if (v) {
return p.resolve(v);
}
if (e) {
return p.reject(e);
}
var unresolved = arr.reduce(function(cnt, v) {
if (v && v.then) {
return cnt + 1;
}
return cnt;
}, 0);
if (unresolved === 0) {
p.resolve(arr);
}
arr.map(function(v, i) {
if (v && v.then) {
v.then(function(r) {
done(null, r);
}, done);
}
});
}
done();
return p;
}
Promise._p = 1;
`

View File

@ -0,0 +1,31 @@
package promise // import "fknsrs.biz/p/ottoext/promise"
import (
"github.com/robertkrimen/otto"
"fknsrs.biz/p/ottoext/loop"
"fknsrs.biz/p/ottoext/timers"
)
func Define(vm *otto.Otto, l *loop.Loop) error {
if v, err := vm.Get("Promise"); err != nil {
return err
} else if !v.IsUndefined() {
return nil
}
if err := timers.Define(vm, l); err != nil {
return err
}
s, err := vm.Compile("promise-bundle.js", src)
if err != nil {
return err
}
if _, err := vm.Run(s); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,236 @@
package repl
import (
"fmt"
"strings"
"github.com/robertkrimen/otto"
)
func seenWith(seen map[otto.Value]bool, v otto.Value) map[otto.Value]bool {
r := make(map[otto.Value]bool)
for k, v := range seen {
r[k] = v
}
r[v] = true
return r
}
func format(v otto.Value, width, indent, limit int) (string, error) {
return formatIndent(v, width, indent, limit, 0, 0, make(map[otto.Value]bool))
}
func formatIndent(v otto.Value, width, indent, limit, level, additional int, seen map[otto.Value]bool) (string, error) {
if limit == 0 {
return "...", nil
}
switch {
case v.IsBoolean(), v.IsNull(), v.IsNumber(), v.IsUndefined():
return v.String(), nil
case v.IsString():
return fmt.Sprintf("%q", v.String()), nil
case v.IsFunction():
n, err := v.Object().Get("name")
if err != nil {
return "", err
}
if n.IsUndefined() {
return "function", nil
}
return fmt.Sprintf("function %s", n.String()), nil
case v.IsObject():
if d, err := formatOneLine(v, limit, seen); err != nil {
return "", err
} else if level*indent+additional+len(d) <= width {
return d, nil
}
switch v.Class() {
case "Array":
return formatArray(v, width, indent, limit, level, seen)
default:
return formatObject(v, width, indent, limit, level, seen)
}
default:
return "", fmt.Errorf("couldn't format type %s", v.Class())
}
}
func formatArray(v otto.Value, width, indent, limit, level int, seen map[otto.Value]bool) (string, error) {
if seen[v] {
return strings.Repeat(" ", level*indent) + "[circular]", nil
}
o := v.Object()
lv, err := o.Get("length")
if err != nil {
return "", err
}
li, err := lv.Export()
if err != nil {
return "", err
}
l, ok := li.(uint32)
if !ok {
return "", fmt.Errorf("length property must be a number; was %T", li)
}
bits := []string{"["}
for i := 0; i < int(l); i++ {
e, err := o.Get(fmt.Sprintf("%d", i))
if err != nil {
return "", err
}
d, err := formatIndent(e, width, indent, limit-1, level+1, 0, seenWith(seen, v))
if err != nil {
return "", err
}
bits = append(bits, strings.Repeat(" ", (level+1)*indent)+d+",")
}
bits = append(bits, strings.Repeat(" ", level*indent)+"]")
return strings.Join(bits, "\n"), nil
}
func formatObject(v otto.Value, width, indent, limit, level int, seen map[otto.Value]bool) (string, error) {
if seen[v] {
return strings.Repeat(" ", level*indent) + "[circular]", nil
}
o := v.Object()
bits := []string{"{"}
keys := o.Keys()
for i, k := range keys {
e, err := o.Get(k)
d, err := formatIndent(e, width, indent, limit-1, level+1, len(k)+2, seenWith(seen, v))
if err != nil {
return "", err
}
bits = append(bits, strings.Repeat(" ", (level+1)*indent)+k+": "+d+",")
i++
}
bits = append(bits, strings.Repeat(" ", level*indent)+"}")
return strings.Join(bits, "\n"), nil
}
func formatOneLine(v otto.Value, limit int, seen map[otto.Value]bool) (string, error) {
if limit == 0 {
return "...", nil
}
switch {
case v.IsBoolean(), v.IsNull(), v.IsNumber(), v.IsUndefined():
return v.String(), nil
case v.IsString():
return fmt.Sprintf("%q", v.String()), nil
case v.IsFunction():
n, err := v.Object().Get("name")
if err != nil {
return "", err
}
if n.IsUndefined() {
return "function", nil
}
return fmt.Sprintf("function %s", n.String()), nil
case v.IsObject():
switch v.Class() {
case "Array":
return formatArrayOneLine(v, limit, seen)
default:
return formatObjectOneLine(v, limit, seen)
}
default:
return "", fmt.Errorf("couldn't format type %s", v.Class())
}
}
func formatArrayOneLine(v otto.Value, limit int, seen map[otto.Value]bool) (string, error) {
if limit == 0 {
return "...", nil
}
if seen[v] {
return "[circular]", nil
}
o := v.Object()
lv, err := o.Get("length")
if err != nil {
return "", err
}
li, err := lv.Export()
if err != nil {
return "", err
}
l, ok := li.(uint32)
if !ok {
return "", fmt.Errorf("length property must be a number; was %T", li)
}
var bits []string
for i := 0; i < int(l); i++ {
e, err := o.Get(fmt.Sprintf("%d", i))
if err != nil {
return "", err
}
d, err := formatOneLine(e, limit-1, seenWith(seen, v))
if err != nil {
return "", err
}
bits = append(bits, d)
}
return "[" + strings.Join(bits, ", ") + "]", nil
}
func formatObjectOneLine(v otto.Value, limit int, seen map[otto.Value]bool) (string, error) {
if limit == 0 {
return "...", nil
}
if seen[v] {
return "[circular]", nil
}
o := v.Object()
bits := []string{}
keys := o.Keys()
for _, k := range keys {
e, err := o.Get(k)
if err != nil {
return "", err
}
d, err := formatOneLine(e, limit-1, seenWith(seen, v))
if err != nil {
return "", err
}
bits = append(bits, k+": "+d)
}
return "{" + strings.Join(bits, ", ") + "}", nil
}

151
vendor/fknsrs.biz/p/ottoext/repl/repl.go vendored Normal file
View File

@ -0,0 +1,151 @@
// Package repl implements an event loop aware REPL (read-eval-print loop)
// for otto.
package repl // import "fknsrs.biz/p/ottoext/repl"
import (
"fmt"
"io"
"strings"
"fknsrs.biz/p/ottoext/loop"
"fknsrs.biz/p/ottoext/loop/looptask"
"github.com/robertkrimen/otto"
"github.com/robertkrimen/otto/parser"
"gopkg.in/readline.v1"
)
// Run creates a REPL with the default prompt and no prelude.
func Run(l *loop.Loop) error {
return RunWithPromptAndPrelude(l, "", "")
}
// RunWithPrompt runs a REPL with the given prompt and no prelude.
func RunWithPrompt(l *loop.Loop, prompt string) error {
return RunWithPromptAndPrelude(l, prompt, "")
}
// RunWithPrelude runs a REPL with the default prompt and the given prelude.
func RunWithPrelude(l *loop.Loop, prelude string) error {
return RunWithPromptAndPrelude(l, "", prelude)
}
// RunWithPromptAndPrelude runs a REPL with the given prompt and prelude.
func RunWithPromptAndPrelude(l *loop.Loop, prompt, prelude string) error {
if prompt == "" {
prompt = ">"
}
prompt = strings.Trim(prompt, " ")
prompt += " "
rl, err := readline.New(prompt)
if err != nil {
return err
}
l.VM().Set("console", map[string]interface{}{
"log": func(c otto.FunctionCall) otto.Value {
s := make([]string, len(c.ArgumentList))
for i := 0; i < len(c.ArgumentList); i++ {
s[i] = c.Argument(i).String()
}
rl.Stdout().Write([]byte(strings.Join(s, " ") + "\n"))
rl.Refresh()
return otto.UndefinedValue()
},
"warn": func(c otto.FunctionCall) otto.Value {
s := make([]string, len(c.ArgumentList))
for i := 0; i < len(c.ArgumentList); i++ {
s[i] = c.Argument(i).String()
}
rl.Stderr().Write([]byte(strings.Join(s, " ") + "\n"))
rl.Refresh()
return otto.UndefinedValue()
},
})
if prelude != "" {
if _, err := io.Copy(rl.Stderr(), strings.NewReader(prelude+"\n")); err != nil {
return err
}
rl.Refresh()
}
var d []string
for {
ll, err := rl.Readline()
if err != nil {
if err == readline.ErrInterrupt {
if d != nil {
d = nil
rl.SetPrompt(prompt)
rl.Refresh()
continue
}
break
}
return err
}
if len(d) == 0 && ll == "" {
continue
}
d = append(d, ll)
s := strings.Join(d, "\n")
if _, err := parser.ParseFile(nil, "repl", s, 0); err != nil {
rl.SetPrompt(strings.Repeat(" ", len(prompt)))
} else {
rl.SetPrompt(prompt)
d = nil
t := looptask.NewEvalTask(s)
// don't report errors to the loop - this lets us handle them and
// resume normal operation
t.SoftError = true
l.Add(t)
l.Ready(t)
v, err := <-t.Value, <-t.Error
if err != nil {
if oerr, ok := err.(*otto.Error); ok {
io.Copy(rl.Stdout(), strings.NewReader(oerr.String()))
} else {
io.Copy(rl.Stdout(), strings.NewReader(err.Error()))
}
} else {
f, err := format(v, 80, 2, 5)
if err != nil {
panic(err)
}
rl.Stdout().Write([]byte("\r" + f + "\n"))
}
}
rl.Refresh()
}
return rl.Close()
}
func inspect(v otto.Value, width, indent int) string {
switch {
case v.IsBoolean(), v.IsNull(), v.IsNumber(), v.IsString(), v.IsUndefined(), v.IsNaN():
return fmt.Sprintf("%s%q", strings.Repeat(" ", indent), v.String())
default:
return ""
}
}

View File

@ -0,0 +1,130 @@
package timers // import "fknsrs.biz/p/ottoext/timers"
import (
"time"
"github.com/robertkrimen/otto"
"fknsrs.biz/p/ottoext/loop"
)
var minDelay = map[bool]int64{
true: 10,
false: 4,
}
func Define(vm *otto.Otto, l *loop.Loop) error {
if v, err := vm.Get("setTimeout"); err != nil {
return err
} else if !v.IsUndefined() {
return nil
}
newTimer := func(interval bool) func(call otto.FunctionCall) otto.Value {
return func(call otto.FunctionCall) otto.Value {
delay, _ := call.Argument(1).ToInteger()
if delay < minDelay[interval] {
delay = minDelay[interval]
}
t := &timerTask{
duration: time.Duration(delay) * time.Millisecond,
call: call,
interval: interval,
}
l.Add(t)
t.timer = time.AfterFunc(t.duration, func() {
l.Ready(t)
})
value, err := call.Otto.ToValue(t)
if err != nil {
panic(err)
}
return value
}
}
vm.Set("setTimeout", newTimer(false))
vm.Set("setInterval", newTimer(true))
vm.Set("setImmediate", func(call otto.FunctionCall) otto.Value {
t := &timerTask{
duration: time.Millisecond,
call: call,
}
l.Add(t)
t.timer = time.AfterFunc(t.duration, func() {
l.Ready(t)
})
value, err := call.Otto.ToValue(t)
if err != nil {
panic(err)
}
return value
})
clearTimeout := func(call otto.FunctionCall) otto.Value {
v, _ := call.Argument(0).Export()
if t, ok := v.(*timerTask); ok {
t.stopped = true
t.timer.Stop()
l.Remove(t)
}
return otto.UndefinedValue()
}
vm.Set("clearTimeout", clearTimeout)
vm.Set("clearInterval", clearTimeout)
vm.Set("clearImmediate", clearTimeout)
return nil
}
type timerTask struct {
id int64
timer *time.Timer
duration time.Duration
interval bool
call otto.FunctionCall
stopped bool
}
func (t *timerTask) SetID(id int64) { t.id = id }
func (t *timerTask) GetID() int64 { return t.id }
func (t *timerTask) Execute(vm *otto.Otto, l *loop.Loop) error {
var arguments []interface{}
if len(t.call.ArgumentList) > 2 {
tmp := t.call.ArgumentList[2:]
arguments = make([]interface{}, 2+len(tmp))
for i, value := range tmp {
arguments[i+2] = value
}
} else {
arguments = make([]interface{}, 1)
}
arguments[0] = t.call.ArgumentList[0]
if _, err := vm.Call(`Function.call.call`, nil, arguments...); err != nil {
return err
}
if t.interval && !t.stopped {
t.timer.Reset(t.duration)
l.Add(t)
}
return nil
}
func (t *timerTask) Cancel() {
t.timer.Stop()
}

8
vendor/github.com/GeertJohan/go.rice/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
/example/example
/example/example.exe
/rice/rice
/rice/rice.exe
*.rice-box.go
*.rice-box.syso
.wercker

19
vendor/github.com/GeertJohan/go.rice/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,19 @@
language: go
go:
- master
- 1.x.x
- 1.8.x
- 1.7.x
- 1.6.x
- 1.5.x
install:
- go get -t ./...
- env
- if [ "${TRAVIS_GO_VERSION%.*}" != "1.5" ]; then go get github.com/golang/lint/golint; fi
script:
- go build -x ./...
- go test -cover ./...
- go vet ./...
- if [ "${TRAVIS_GO_VERSION%.*}" != "1.5" ]; then golint .; fi

4
vendor/github.com/GeertJohan/go.rice/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,4 @@
Geert-Johan Riemer <geertjohan@geertjohan.net>
Paul Maddox <paul.maddox@gmail.com>
Vincent Petithory <vincent.petithory@gmail.com>

22
vendor/github.com/GeertJohan/go.rice/LICENSE generated vendored Normal file
View File

@ -0,0 +1,22 @@
Copyright (c) 2013, Geert-Johan Riemer
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

151
vendor/github.com/GeertJohan/go.rice/README.md generated vendored Normal file
View File

@ -0,0 +1,151 @@
## go.rice
[![Build Status](https://travis-ci.org/GeertJohan/go.rice.png)](https://travis-ci.org/GeertJohan/go.rice)
[![Godoc](https://img.shields.io/badge/godoc-go.rice-blue.svg?style=flat-square)](https://godoc.org/github.com/GeertJohan/go.rice)
go.rice is a [Go](http://golang.org) package that makes working with resources such as html,js,css,images and templates very easy. During development `go.rice` will load required files directly from disk. Upon deployment it is easy to add all resource files to a executable using the `rice` tool, without changing the source code for your package. go.rice provides several methods to add resources to a binary.
### What does it do?
The first thing go.rice does is finding the correct absolute path for your resource files. Say you are executing go binary in your home directory, but your `html-files` are located in `$GOPATH/src/yourApplication/html-files`. `go.rice` will lookup the correct path for that directory (relative to the location of yourApplication). The only thing you have to do is include the resources using `rice.FindBox("html-files")`.
This only works when the source is available to the machine executing the binary. Which is always the case when the binary was installed with `go get` or `go install`. It might occur that you wish to simply provide a binary, without source. The `rice` tool analyses source code and finds call's to `rice.FindBox(..)` and adds the required directories to the executable binary. There are several methods to add these resources. You can 'embed' by generating go source code, or append the resource to the executable as zip file. In both cases `go.rice` will detect the embedded or appended resources and load those, instead of looking up files from disk.
### Installation
Use `go get` to install the package the `rice` tool.
```
go get github.com/GeertJohan/go.rice
go get github.com/GeertJohan/go.rice/rice
```
### Package usage
Import the package: `import "github.com/GeertJohan/go.rice"`
**Serving a static content folder over HTTP with a rice Box**
```go
http.Handle("/", http.FileServer(rice.MustFindBox("http-files").HTTPBox()))
http.ListenAndServe(":8080", nil)
```
**Service a static content folder over HTTP at a non-root location**
```go
box := rice.MustFindBox("cssfiles")
cssFileServer := http.StripPrefix("/css/", http.FileServer(box.HTTPBox()))
http.Handle("/css/", cssFileServer)
http.ListenAndServe(":8080", nil)
```
Note the *trailing slash* in `/css/` in both the call to
`http.StripPrefix` and `http.Handle`.
**Loading a template**
```go
// find a rice.Box
templateBox, err := rice.FindBox("example-templates")
if err != nil {
log.Fatal(err)
}
// get file contents as string
templateString, err := templateBox.String("message.tmpl")
if err != nil {
log.Fatal(err)
}
// parse and execute the template
tmplMessage, err := template.New("message").Parse(templateString)
if err != nil {
log.Fatal(err)
}
tmplMessage.Execute(os.Stdout, map[string]string{"Message": "Hello, world!"})
```
Never call `FindBox()` or `MustFindBox()` from an `init()` function, as the boxes might have not been loaded at that time.
### Tool usage
The `rice` tool lets you add the resources to a binary executable so the files are not loaded from the filesystem anymore. This creates a 'standalone' executable. There are several ways to add the resources to a binary, each has pro's and con's but all will work without requiring changes to the way you load the resources.
#### embed-go
**Embed resources by generating Go source code**
This method must be executed before building. It generates a single Go source file called *rice-box.go* for each package, that is compiled by the go compiler into the binary.
The downside with this option is that the generated go source files can become very large, which will slow down compilation and require lots of memory to compile.
Execute the following commands:
```
rice embed-go
go build
```
*A Note on Symbolic Links*: `embed-go` uses the `os.Walk` function
from the standard library. The `os.Walk` function does **not** follow
symbolic links. So, when creating a box, be aware that any symbolic
links inside your box's directory will not be followed. **However**,
if the box itself is a symbolic link, its actual location will be
resolved first and then walked. In summary, if your box location is a
symbolic link, it will be followed but none of the symbolic links in
the box will be followed.
#### embed-syso
**Embed resources by generating a coff .syso file and some .go source code**
** This method is experimental and should not be used for production systems just yet **
This method must be executed before building. It generates a COFF .syso file and Go source file that are compiled by the go compiler into the binary.
Execute the following commands:
```
rice embed-syso
go build
```
#### append
**Append resources to executable as zip file**
This method changes an already built executable. It appends the resources as zip file to the binary. It makes compilation a lot faster and can be used with large resource files.
Downsides for appending are that it requires `zip` to be installed and does not provide a working Seek method.
Run the following commands to create a standalone executable.
```
go build -o example
rice append --exec example
```
**Note: requires zip command to be installed**
On windows, install zip from http://gnuwin32.sourceforge.net/packages/zip.htm or cygwin/msys toolsets.
#### Help information
Run `rice -h` for information about all options.
You can run the -h option for each sub-command, e.g. `rice append -h`.
### Order of precedence
When opening a new box, the rice package tries to locate the resources in the following order:
- embedded in generated go source
- appended as zip
- 'live' from filesystem
### License
This project is licensed under a Simplified BSD license. Please read the [LICENSE file][license].
### TODO & Development
This package is not completed yet. Though it already provides working embedding, some important featuers are still missing.
- implement Readdir() correctly on virtualDir
- in-code TODO's
- find boxes in imported packages
Less important stuff:
- idea, os/arch dependent embeds. rice checks if embedding file has _os_arch or build flags. If box is not requested by file without buildflags, then the buildflags are applied to the embed file.
### Package documentation
You will find package documentation at [godoc.org/github.com/GeertJohan/go.rice][godoc].
[license]: https://github.com/GeertJohan/go.rice/blob/master/LICENSE
[godoc]: http://godoc.org/github.com/GeertJohan/go.rice

138
vendor/github.com/GeertJohan/go.rice/appended.go generated vendored Normal file
View File

@ -0,0 +1,138 @@
package rice
import (
"archive/zip"
"log"
"os"
"path/filepath"
"strings"
"time"
"github.com/daaku/go.zipexe"
"github.com/kardianos/osext"
)
// appendedBox defines an appended box
type appendedBox struct {
Name string // box name
Files map[string]*appendedFile // appended files (*zip.File) by full path
}
type appendedFile struct {
zipFile *zip.File
dir bool
dirInfo *appendedDirInfo
children []*appendedFile
content []byte
}
// appendedBoxes is a public register of appendes boxes
var appendedBoxes = make(map[string]*appendedBox)
func init() {
// find if exec is appended
thisFile, err := osext.Executable()
if err != nil {
return // not appended or cant find self executable
}
closer, rd, err := zipexe.OpenCloser(thisFile)
if err != nil {
return // not appended
}
defer closer.Close()
for _, f := range rd.File {
// get box and file name from f.Name
fileParts := strings.SplitN(strings.TrimLeft(filepath.ToSlash(f.Name), "/"), "/", 2)
boxName := fileParts[0]
var fileName string
if len(fileParts) > 1 {
fileName = fileParts[1]
}
// find box or create new one if doesn't exist
box := appendedBoxes[boxName]
if box == nil {
box = &appendedBox{
Name: boxName,
Files: make(map[string]*appendedFile),
}
appendedBoxes[boxName] = box
}
// create and add file to box
af := &appendedFile{
zipFile: f,
}
if f.Comment == "dir" {
af.dir = true
af.dirInfo = &appendedDirInfo{
name: filepath.Base(af.zipFile.Name),
//++ TODO: use zip modtime when that is set correctly: af.zipFile.ModTime()
time: time.Now(),
}
} else {
// this is a file, we need it's contents so we can create a bytes.Reader when the file is opened
// make a new byteslice
af.content = make([]byte, af.zipFile.FileInfo().Size())
// ignore reading empty files from zip (empty file still is a valid file to be read though!)
if len(af.content) > 0 {
// open io.ReadCloser
rc, err := af.zipFile.Open()
if err != nil {
af.content = nil // this will cause an error when the file is being opened or seeked (which is good)
// TODO: it's quite blunt to just log this stuff. but this is in init, so rice.Debug can't be changed yet..
log.Printf("error opening appended file %s: %v", af.zipFile.Name, err)
} else {
_, err = rc.Read(af.content)
rc.Close()
if err != nil {
af.content = nil // this will cause an error when the file is being opened or seeked (which is good)
// TODO: it's quite blunt to just log this stuff. but this is in init, so rice.Debug can't be changed yet..
log.Printf("error reading data for appended file %s: %v", af.zipFile.Name, err)
}
}
}
}
// add appendedFile to box file list
box.Files[fileName] = af
// add to parent dir (if any)
dirName := filepath.Dir(fileName)
if dirName == "." {
dirName = ""
}
if fileName != "" { // don't make box root dir a child of itself
if dir := box.Files[dirName]; dir != nil {
dir.children = append(dir.children, af)
}
}
}
}
// implements os.FileInfo.
// used for Readdir()
type appendedDirInfo struct {
name string
time time.Time
}
func (adi *appendedDirInfo) Name() string {
return adi.name
}
func (adi *appendedDirInfo) Size() int64 {
return 0
}
func (adi *appendedDirInfo) Mode() os.FileMode {
return os.ModeDir
}
func (adi *appendedDirInfo) ModTime() time.Time {
return adi.time
}
func (adi *appendedDirInfo) IsDir() bool {
return true
}
func (adi *appendedDirInfo) Sys() interface{} {
return nil
}

337
vendor/github.com/GeertJohan/go.rice/box.go generated vendored Normal file
View File

@ -0,0 +1,337 @@
package rice
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/GeertJohan/go.rice/embedded"
)
// Box abstracts a directory for resources/files.
// It can either load files from disk, or from embedded code (when `rice --embed` was ran).
type Box struct {
name string
absolutePath string
embed *embedded.EmbeddedBox
appendd *appendedBox
}
var defaultLocateOrder = []LocateMethod{LocateEmbedded, LocateAppended, LocateFS}
func findBox(name string, order []LocateMethod) (*Box, error) {
b := &Box{name: name}
// no support for absolute paths since gopath can be different on different machines.
// therefore, required box must be located relative to package requiring it.
if filepath.IsAbs(name) {
return nil, errors.New("given name/path is absolute")
}
var err error
for _, method := range order {
switch method {
case LocateEmbedded:
if embed := embedded.EmbeddedBoxes[name]; embed != nil {
b.embed = embed
return b, nil
}
case LocateAppended:
appendedBoxName := strings.Replace(name, `/`, `-`, -1)
if appendd := appendedBoxes[appendedBoxName]; appendd != nil {
b.appendd = appendd
return b, nil
}
case LocateFS:
// resolve absolute directory path
err := b.resolveAbsolutePathFromCaller()
if err != nil {
continue
}
// check if absolutePath exists on filesystem
info, err := os.Stat(b.absolutePath)
if err != nil {
continue
}
// check if absolutePath is actually a directory
if !info.IsDir() {
err = errors.New("given name/path is not a directory")
continue
}
return b, nil
case LocateWorkingDirectory:
// resolve absolute directory path
err := b.resolveAbsolutePathFromWorkingDirectory()
if err != nil {
continue
}
// check if absolutePath exists on filesystem
info, err := os.Stat(b.absolutePath)
if err != nil {
continue
}
// check if absolutePath is actually a directory
if !info.IsDir() {
err = errors.New("given name/path is not a directory")
continue
}
return b, nil
}
}
if err == nil {
err = fmt.Errorf("could not locate box %q", name)
}
return nil, err
}
// FindBox returns a Box instance for given name.
// When the given name is a relative path, it's base path will be the calling pkg/cmd's source root.
// When the given name is absolute, it's absolute. derp.
// Make sure the path doesn't contain any sensitive information as it might be placed into generated go source (embedded).
func FindBox(name string) (*Box, error) {
return findBox(name, defaultLocateOrder)
}
// MustFindBox returns a Box instance for given name, like FindBox does.
// It does not return an error, instead it panics when an error occurs.
func MustFindBox(name string) *Box {
box, err := findBox(name, defaultLocateOrder)
if err != nil {
panic(err)
}
return box
}
// This is injected as a mutable function literal so that we can mock it out in
// tests and return a fixed test file.
var resolveAbsolutePathFromCaller = func(name string, nStackFrames int) (string, error) {
_, callingGoFile, _, ok := runtime.Caller(nStackFrames)
if !ok {
return "", errors.New("couldn't find caller on stack")
}
// resolve to proper path
pkgDir := filepath.Dir(callingGoFile)
// fix for go cover
const coverPath = "_test/_obj_test"
if !filepath.IsAbs(pkgDir) {
if i := strings.Index(pkgDir, coverPath); i >= 0 {
pkgDir = pkgDir[:i] + pkgDir[i+len(coverPath):] // remove coverPath
pkgDir = filepath.Join(os.Getenv("GOPATH"), "src", pkgDir) // make absolute
}
}
return filepath.Join(pkgDir, name), nil
}
func (b *Box) resolveAbsolutePathFromCaller() error {
path, err := resolveAbsolutePathFromCaller(b.name, 4)
if err != nil {
return err
}
b.absolutePath = path
return nil
}
func (b *Box) resolveAbsolutePathFromWorkingDirectory() error {
path, err := os.Getwd()
if err != nil {
return err
}
b.absolutePath = filepath.Join(path, b.name)
return nil
}
// IsEmbedded indicates wether this box was embedded into the application
func (b *Box) IsEmbedded() bool {
return b.embed != nil
}
// IsAppended indicates wether this box was appended to the application
func (b *Box) IsAppended() bool {
return b.appendd != nil
}
// Time returns how actual the box is.
// When the box is embedded, it's value is saved in the embedding code.
// When the box is live, this methods returns time.Now()
func (b *Box) Time() time.Time {
if b.IsEmbedded() {
return b.embed.Time
}
//++ TODO: return time for appended box
return time.Now()
}
// Open opens a File from the box
// If there is an error, it will be of type *os.PathError.
func (b *Box) Open(name string) (*File, error) {
if Debug {
fmt.Printf("Open(%s)\n", name)
}
if b.IsEmbedded() {
if Debug {
fmt.Println("Box is embedded")
}
// trim prefix (paths are relative to box)
name = strings.TrimLeft(name, "/")
if Debug {
fmt.Printf("Trying %s\n", name)
}
// search for file
ef := b.embed.Files[name]
if ef == nil {
if Debug {
fmt.Println("Didn't find file in embed")
}
// file not found, try dir
ed := b.embed.Dirs[name]
if ed == nil {
if Debug {
fmt.Println("Didn't find dir in embed")
}
// dir not found, error out
return nil, &os.PathError{
Op: "open",
Path: name,
Err: os.ErrNotExist,
}
}
if Debug {
fmt.Println("Found dir. Returning virtual dir")
}
vd := newVirtualDir(ed)
return &File{virtualD: vd}, nil
}
// box is embedded
if Debug {
fmt.Println("Found file. Returning virtual file")
}
vf := newVirtualFile(ef)
return &File{virtualF: vf}, nil
}
if b.IsAppended() {
// trim prefix (paths are relative to box)
name = strings.TrimLeft(name, "/")
// search for file
appendedFile := b.appendd.Files[name]
if appendedFile == nil {
return nil, &os.PathError{
Op: "open",
Path: name,
Err: os.ErrNotExist,
}
}
// create new file
f := &File{
appendedF: appendedFile,
}
// if this file is a directory, we want to be able to read and seek
if !appendedFile.dir {
// looks like malformed data in zip, error now
if appendedFile.content == nil {
return nil, &os.PathError{
Op: "open",
Path: "name",
Err: errors.New("error reading data from zip file"),
}
}
// create new bytes.Reader
f.appendedFileReader = bytes.NewReader(appendedFile.content)
}
// all done
return f, nil
}
// perform os open
if Debug {
fmt.Printf("Using os.Open(%s)", filepath.Join(b.absolutePath, name))
}
file, err := os.Open(filepath.Join(b.absolutePath, name))
if err != nil {
return nil, err
}
return &File{realF: file}, nil
}
// Bytes returns the content of the file with given name as []byte.
func (b *Box) Bytes(name string) ([]byte, error) {
file, err := b.Open(name)
if err != nil {
return nil, err
}
defer file.Close()
content, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
return content, nil
}
// MustBytes returns the content of the file with given name as []byte.
// panic's on error.
func (b *Box) MustBytes(name string) []byte {
bts, err := b.Bytes(name)
if err != nil {
panic(err)
}
return bts
}
// String returns the content of the file with given name as string.
func (b *Box) String(name string) (string, error) {
// check if box is embedded, optimized fast path
if b.IsEmbedded() {
// find file in embed
ef := b.embed.Files[name]
if ef == nil {
return "", os.ErrNotExist
}
// return as string
return ef.Content, nil
}
bts, err := b.Bytes(name)
if err != nil {
return "", err
}
return string(bts), nil
}
// MustString returns the content of the file with given name as string.
// panic's on error.
func (b *Box) MustString(name string) string {
str, err := b.String(name)
if err != nil {
panic(err)
}
return str
}
// Name returns the name of the box
func (b *Box) Name() string {
return b.name
}

39
vendor/github.com/GeertJohan/go.rice/config.go generated vendored Normal file
View File

@ -0,0 +1,39 @@
package rice
// LocateMethod defines how a box is located.
type LocateMethod int
const (
LocateFS = LocateMethod(iota) // Locate on the filesystem according to package path.
LocateAppended // Locate boxes appended to the executable.
LocateEmbedded // Locate embedded boxes.
LocateWorkingDirectory // Locate on the binary working directory
)
// Config allows customizing the box lookup behavior.
type Config struct {
// LocateOrder defines the priority order that boxes are searched for. By
// default, the package global FindBox searches for embedded boxes first,
// then appended boxes, and then finally boxes on the filesystem. That
// search order may be customized by provided the ordered list here. Leaving
// out a particular method will omit that from the search space. For
// example, []LocateMethod{LocateEmbedded, LocateAppended} will never search
// the filesystem for boxes.
LocateOrder []LocateMethod
}
// FindBox searches for boxes using the LocateOrder of the config.
func (c *Config) FindBox(boxName string) (*Box, error) {
return findBox(boxName, c.LocateOrder)
}
// MustFindBox searches for boxes using the LocateOrder of the config, like
// FindBox does. It does not return an error, instead it panics when an error
// occurs.
func (c *Config) MustFindBox(boxName string) *Box {
box, err := findBox(boxName, c.LocateOrder)
if err != nil {
panic(err)
}
return box
}

4
vendor/github.com/GeertJohan/go.rice/debug.go generated vendored Normal file
View File

@ -0,0 +1,4 @@
package rice
// Debug can be set to true to enable debugging.
var Debug = false

90
vendor/github.com/GeertJohan/go.rice/embedded.go generated vendored Normal file
View File

@ -0,0 +1,90 @@
package rice
import (
"os"
"time"
"github.com/GeertJohan/go.rice/embedded"
)
// re-type to make exported methods invisible to user (godoc)
// they're not required for the user
// embeddedDirInfo implements os.FileInfo
type embeddedDirInfo embedded.EmbeddedDir
// Name returns the base name of the directory
// (implementing os.FileInfo)
func (ed *embeddedDirInfo) Name() string {
return ed.Filename
}
// Size always returns 0
// (implementing os.FileInfo)
func (ed *embeddedDirInfo) Size() int64 {
return 0
}
// Mode returns the file mode bits
// (implementing os.FileInfo)
func (ed *embeddedDirInfo) Mode() os.FileMode {
return os.FileMode(0555 | os.ModeDir) // dr-xr-xr-x
}
// ModTime returns the modification time
// (implementing os.FileInfo)
func (ed *embeddedDirInfo) ModTime() time.Time {
return ed.DirModTime
}
// IsDir returns the abbreviation for Mode().IsDir() (always true)
// (implementing os.FileInfo)
func (ed *embeddedDirInfo) IsDir() bool {
return true
}
// Sys returns the underlying data source (always nil)
// (implementing os.FileInfo)
func (ed *embeddedDirInfo) Sys() interface{} {
return nil
}
// re-type to make exported methods invisible to user (godoc)
// they're not required for the user
// embeddedFileInfo implements os.FileInfo
type embeddedFileInfo embedded.EmbeddedFile
// Name returns the base name of the file
// (implementing os.FileInfo)
func (ef *embeddedFileInfo) Name() string {
return ef.Filename
}
// Size returns the length in bytes for regular files; system-dependent for others
// (implementing os.FileInfo)
func (ef *embeddedFileInfo) Size() int64 {
return int64(len(ef.Content))
}
// Mode returns the file mode bits
// (implementing os.FileInfo)
func (ef *embeddedFileInfo) Mode() os.FileMode {
return os.FileMode(0555) // r-xr-xr-x
}
// ModTime returns the modification time
// (implementing os.FileInfo)
func (ef *embeddedFileInfo) ModTime() time.Time {
return ef.FileModTime
}
// IsDir returns the abbreviation for Mode().IsDir() (always false)
// (implementing os.FileInfo)
func (ef *embeddedFileInfo) IsDir() bool {
return false
}
// Sys returns the underlying data source (always nil)
// (implementing os.FileInfo)
func (ef *embeddedFileInfo) Sys() interface{} {
return nil
}

View File

@ -0,0 +1,80 @@
// Package embedded defines embedded data types that are shared between the go.rice package and generated code.
package embedded
import (
"fmt"
"path/filepath"
"strings"
"time"
)
const (
EmbedTypeGo = 0
EmbedTypeSyso = 1
)
// EmbeddedBox defines an embedded box
type EmbeddedBox struct {
Name string // box name
Time time.Time // embed time
EmbedType int // kind of embedding
Files map[string]*EmbeddedFile // ALL embedded files by full path
Dirs map[string]*EmbeddedDir // ALL embedded dirs by full path
}
// Link creates the ChildDirs and ChildFiles links in all EmbeddedDir's
func (e *EmbeddedBox) Link() {
for path, ed := range e.Dirs {
fmt.Println(path)
ed.ChildDirs = make([]*EmbeddedDir, 0)
ed.ChildFiles = make([]*EmbeddedFile, 0)
}
for path, ed := range e.Dirs {
parentDirpath, _ := filepath.Split(path)
if strings.HasSuffix(parentDirpath, "/") {
parentDirpath = parentDirpath[:len(parentDirpath)-1]
}
parentDir := e.Dirs[parentDirpath]
if parentDir == nil {
panic("parentDir `" + parentDirpath + "` is missing in embedded box")
}
parentDir.ChildDirs = append(parentDir.ChildDirs, ed)
}
for path, ef := range e.Files {
dirpath, _ := filepath.Split(path)
if strings.HasSuffix(dirpath, "/") {
dirpath = dirpath[:len(dirpath)-1]
}
dir := e.Dirs[dirpath]
if dir == nil {
panic("dir `" + dirpath + "` is missing in embedded box")
}
dir.ChildFiles = append(dir.ChildFiles, ef)
}
}
// EmbeddedDir is instanced in the code generated by the rice tool and contains all necicary information about an embedded file
type EmbeddedDir struct {
Filename string
DirModTime time.Time
ChildDirs []*EmbeddedDir // direct childs, as returned by virtualDir.Readdir()
ChildFiles []*EmbeddedFile // direct childs, as returned by virtualDir.Readdir()
}
// EmbeddedFile is instanced in the code generated by the rice tool and contains all necicary information about an embedded file
type EmbeddedFile struct {
Filename string // filename
FileModTime time.Time
Content string
}
// EmbeddedBoxes is a public register of embedded boxes
var EmbeddedBoxes = make(map[string]*EmbeddedBox)
// RegisterEmbeddedBox registers an EmbeddedBox
func RegisterEmbeddedBox(name string, box *EmbeddedBox) {
if _, exists := EmbeddedBoxes[name]; exists {
panic(fmt.Sprintf("EmbeddedBox with name `%s` exists already", name))
}
EmbeddedBoxes[name] = box
}

View File

@ -0,0 +1,2 @@
test content
break

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -0,0 +1 @@
I have a message for you: {{.Message}}

View File

@ -0,0 +1,69 @@
package main
import (
"encoding/hex"
"fmt"
"log"
"net/http"
"os"
"text/template"
"github.com/GeertJohan/go.rice"
"github.com/davecgh/go-spew/spew"
)
func main() {
conf := rice.Config{
LocateOrder: []rice.LocateMethod{rice.LocateEmbedded, rice.LocateAppended, rice.LocateFS},
}
box, err := conf.FindBox("example-files")
if err != nil {
log.Fatalf("error opening rice.Box: %s\n", err)
}
// spew.Dump(box)
contentString, err := box.String("file.txt")
if err != nil {
log.Fatalf("could not read file contents as string: %s\n", err)
}
log.Printf("Read some file contents as string:\n%s\n", contentString)
contentBytes, err := box.Bytes("file.txt")
if err != nil {
log.Fatalf("could not read file contents as byteSlice: %s\n", err)
}
log.Printf("Read some file contents as byteSlice:\n%s\n", hex.Dump(contentBytes))
file, err := box.Open("file.txt")
if err != nil {
log.Fatalf("could not open file: %s\n", err)
}
spew.Dump(file)
// find/create a rice.Box
templateBox, err := rice.FindBox("example-templates")
if err != nil {
log.Fatal(err)
}
// get file contents as string
templateString, err := templateBox.String("message.tmpl")
if err != nil {
log.Fatal(err)
}
// parse and execute the template
tmplMessage, err := template.New("message").Parse(templateString)
if err != nil {
log.Fatal(err)
}
tmplMessage.Execute(os.Stdout, map[string]string{"Message": "Hello, world!"})
http.Handle("/", http.FileServer(box.HTTPBox()))
go func() {
fmt.Println("Serving files on :8080, press ctrl-C to exit")
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatalf("error serving files: %v", err)
}
}()
select {}
}

144
vendor/github.com/GeertJohan/go.rice/file.go generated vendored Normal file
View File

@ -0,0 +1,144 @@
package rice
import (
"bytes"
"errors"
"os"
"path/filepath"
)
// File implements the io.Reader, io.Seeker, io.Closer and http.File interfaces
type File struct {
// File abstracts file methods so the user doesn't see the difference between rice.virtualFile, rice.virtualDir and os.File
// TODO: maybe use internal File interface and four implementations: *os.File, appendedFile, virtualFile, virtualDir
// real file on disk
realF *os.File
// when embedded (go)
virtualF *virtualFile
virtualD *virtualDir
// when appended (zip)
appendedF *appendedFile
appendedFileReader *bytes.Reader
// TODO: is appendedFileReader subject of races? Might need a lock here..
}
// Close is like (*os.File).Close()
// Visit http://golang.org/pkg/os/#File.Close for more information
func (f *File) Close() error {
if f.appendedF != nil {
if f.appendedFileReader == nil {
return errors.New("already closed")
}
f.appendedFileReader = nil
return nil
}
if f.virtualF != nil {
return f.virtualF.close()
}
if f.virtualD != nil {
return f.virtualD.close()
}
return f.realF.Close()
}
// Stat is like (*os.File).Stat()
// Visit http://golang.org/pkg/os/#File.Stat for more information
func (f *File) Stat() (os.FileInfo, error) {
if f.appendedF != nil {
if f.appendedF.dir {
return f.appendedF.dirInfo, nil
}
if f.appendedFileReader == nil {
return nil, errors.New("file is closed")
}
return f.appendedF.zipFile.FileInfo(), nil
}
if f.virtualF != nil {
return f.virtualF.stat()
}
if f.virtualD != nil {
return f.virtualD.stat()
}
return f.realF.Stat()
}
// Readdir is like (*os.File).Readdir()
// Visit http://golang.org/pkg/os/#File.Readdir for more information
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
if f.appendedF != nil {
if f.appendedF.dir {
fi := make([]os.FileInfo, 0, len(f.appendedF.children))
for _, childAppendedFile := range f.appendedF.children {
if childAppendedFile.dir {
fi = append(fi, childAppendedFile.dirInfo)
} else {
fi = append(fi, childAppendedFile.zipFile.FileInfo())
}
}
return fi, nil
}
//++ TODO: is os.ErrInvalid the correct error for Readdir on file?
return nil, os.ErrInvalid
}
if f.virtualF != nil {
return f.virtualF.readdir(count)
}
if f.virtualD != nil {
return f.virtualD.readdir(count)
}
return f.realF.Readdir(count)
}
// Read is like (*os.File).Read()
// Visit http://golang.org/pkg/os/#File.Read for more information
func (f *File) Read(bts []byte) (int, error) {
if f.appendedF != nil {
if f.appendedFileReader == nil {
return 0, &os.PathError{
Op: "read",
Path: filepath.Base(f.appendedF.zipFile.Name),
Err: errors.New("file is closed"),
}
}
if f.appendedF.dir {
return 0, &os.PathError{
Op: "read",
Path: filepath.Base(f.appendedF.zipFile.Name),
Err: errors.New("is a directory"),
}
}
return f.appendedFileReader.Read(bts)
}
if f.virtualF != nil {
return f.virtualF.read(bts)
}
if f.virtualD != nil {
return f.virtualD.read(bts)
}
return f.realF.Read(bts)
}
// Seek is like (*os.File).Seek()
// Visit http://golang.org/pkg/os/#File.Seek for more information
func (f *File) Seek(offset int64, whence int) (int64, error) {
if f.appendedF != nil {
if f.appendedFileReader == nil {
return 0, &os.PathError{
Op: "seek",
Path: filepath.Base(f.appendedF.zipFile.Name),
Err: errors.New("file is closed"),
}
}
return f.appendedFileReader.Seek(offset, whence)
}
if f.virtualF != nil {
return f.virtualF.seek(offset, whence)
}
if f.virtualD != nil {
return f.virtualD.seek(offset, whence)
}
return f.realF.Seek(offset, whence)
}

21
vendor/github.com/GeertJohan/go.rice/http.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
package rice
import (
"net/http"
)
// HTTPBox implements http.FileSystem which allows the use of Box with a http.FileServer.
// e.g.: http.Handle("/", http.FileServer(rice.MustFindBox("http-files").HTTPBox()))
type HTTPBox struct {
*Box
}
// HTTPBox creates a new HTTPBox from an existing Box
func (b *Box) HTTPBox() *HTTPBox {
return &HTTPBox{b}
}
// Open returns a File using the http.File interface
func (hb *HTTPBox) Open(name string) (http.File, error) {
return hb.Box.Open(name)
}

157
vendor/github.com/GeertJohan/go.rice/rice/append.go generated vendored Normal file
View File

@ -0,0 +1,157 @@
package main
import (
"archive/zip"
"fmt"
"go/build"
"io"
"os"
"path/filepath"
"strings"
"time"
zipexe "github.com/daaku/go.zipexe"
)
func operationAppend(pkgs []*build.Package) {
// create tmp zipfile
tmpZipfileName := filepath.Join(os.TempDir(), fmt.Sprintf("ricebox-%d-%s.zip", time.Now().Unix(), randomString(10)))
verbosef("Will create tmp zipfile: %s\n", tmpZipfileName)
tmpZipfile, err := os.Create(tmpZipfileName)
if err != nil {
fmt.Printf("Error creating tmp zipfile: %s\n", err)
os.Exit(1)
}
defer func() {
tmpZipfile.Close()
os.Remove(tmpZipfileName)
}()
// find abs path for binary file
binfileName, err := filepath.Abs(flags.Append.Executable)
if err != nil {
fmt.Printf("Error finding absolute path for executable to append: %s\n", err)
os.Exit(1)
}
verbosef("Will append to file: %s\n", binfileName)
// check that command doesn't already have zip appended
if rd, _ := zipexe.Open(binfileName); rd != nil {
fmt.Printf("Cannot append to already appended executable. Please remove %s and build a fresh one.\n", binfileName)
os.Exit(1)
}
// open binfile
binfile, err := os.OpenFile(binfileName, os.O_WRONLY, os.ModeAppend)
if err != nil {
fmt.Printf("Error: unable to open executable file: %s\n", err)
os.Exit(1)
}
defer binfile.Close()
binfileInfo, err := binfile.Stat()
if err != nil {
fmt.Printf("Error: unable to stat executable file: %s\n", err)
os.Exit(1)
}
// create zip.Writer
zipWriter := zip.NewWriter(tmpZipfile)
// write the zip offset into the zip data
zipWriter.SetOffset(binfileInfo.Size())
for _, pkg := range pkgs {
// find boxes for this command
boxMap := findBoxes(pkg)
// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
if len(boxMap) == 0 {
fmt.Printf("no calls to rice.FindBox() or rice.MustFindBox() found in import path `%s`\n", pkg.ImportPath)
continue
}
verbosef("\n")
for boxname := range boxMap {
appendedBoxName := strings.Replace(boxname, `/`, `-`, -1)
// walk box path's and insert files
boxPath := filepath.Clean(filepath.Join(pkg.Dir, boxname))
filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
if info == nil {
fmt.Printf("Error: box \"%s\" not found on disk\n", path)
os.Exit(1)
}
// create zipFilename
zipFileName := filepath.Join(appendedBoxName, strings.TrimPrefix(path, boxPath))
// write directories as empty file with comment "dir"
if info.IsDir() {
_, err := zipWriter.CreateHeader(&zip.FileHeader{
Name: zipFileName,
Comment: "dir",
})
if err != nil {
fmt.Printf("Error creating dir in tmp zip: %s\n", err)
os.Exit(1)
}
return nil
}
// create zipFileWriter
zipFileHeader, err := zip.FileInfoHeader(info)
if err != nil {
fmt.Printf("Error creating zip FileHeader: %v\n", err)
os.Exit(1)
}
zipFileHeader.Name = zipFileName
zipFileWriter, err := zipWriter.CreateHeader(zipFileHeader)
if err != nil {
fmt.Printf("Error creating file in tmp zip: %s\n", err)
os.Exit(1)
}
srcFile, err := os.Open(path)
if err != nil {
fmt.Printf("Error opening file to append: %s\n", err)
os.Exit(1)
}
_, err = io.Copy(zipFileWriter, srcFile)
if err != nil {
fmt.Printf("Error copying file contents to zip: %s\n", err)
os.Exit(1)
}
srcFile.Close()
return nil
})
}
}
err = zipWriter.Close()
if err != nil {
fmt.Printf("Error closing tmp zipfile: %s\n", err)
os.Exit(1)
}
err = tmpZipfile.Sync()
if err != nil {
fmt.Printf("Error syncing tmp zipfile: %s\n", err)
os.Exit(1)
}
_, err = tmpZipfile.Seek(0, 0)
if err != nil {
fmt.Printf("Error seeking tmp zipfile: %s\n", err)
os.Exit(1)
}
_, err = binfile.Seek(0, 2)
if err != nil {
fmt.Printf("Error seeking bin file: %s\n", err)
os.Exit(1)
}
_, err = io.Copy(binfile, tmpZipfile)
if err != nil {
fmt.Printf("Error appending zipfile to executable: %s\n", err)
os.Exit(1)
}
}

33
vendor/github.com/GeertJohan/go.rice/rice/clean.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
package main
import (
"fmt"
"go/build"
"os"
"path/filepath"
"strings"
)
func operationClean(pkg *build.Package) {
filepath.Walk(pkg.Dir, func(filename string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("error walking pkg dir to clean files: %v\n", err)
os.Exit(1)
}
if info.IsDir() {
return nil
}
verbosef("checking file '%s'\n", filename)
if filepath.Base(filename) == "rice-box.go" ||
strings.HasSuffix(filename, ".rice-box.go") ||
strings.HasSuffix(filename, ".rice-box.syso") {
err := os.Remove(filename)
if err != nil {
fmt.Printf("error removing file (%s): %s\n", filename, err)
os.Exit(-1)
}
verbosef("removed file '%s'\n", filename)
}
return nil
})
}

161
vendor/github.com/GeertJohan/go.rice/rice/embed-go.go generated vendored Normal file
View File

@ -0,0 +1,161 @@
package main
import (
"bytes"
"fmt"
"go/build"
"go/format"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
)
const boxFilename = "rice-box.go"
func writeBoxesGo(pkg *build.Package, out io.Writer) error {
boxMap := findBoxes(pkg)
// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
if len(boxMap) == 0 {
fmt.Println("no calls to rice.FindBox() found")
return nil
}
verbosef("\n")
var boxes []*boxDataType
for boxname := range boxMap {
// find path and filename for this box
boxPath := filepath.Join(pkg.Dir, boxname)
// Check to see if the path for the box is a symbolic link. If so, simply
// box what the symbolic link points to. Note: the filepath.Walk function
// will NOT follow any nested symbolic links. This only handles the case
// where the root of the box is a symbolic link.
symPath, serr := os.Readlink(boxPath)
if serr == nil {
boxPath = symPath
}
// verbose info
verbosef("embedding box '%s' to '%s'\n", boxname, boxFilename)
// read box metadata
boxInfo, ierr := os.Stat(boxPath)
if ierr != nil {
return fmt.Errorf("Error: unable to access box at %s\n", boxPath)
}
// create box datastructure (used by template)
box := &boxDataType{
BoxName: boxname,
UnixNow: boxInfo.ModTime().Unix(),
Files: make([]*fileDataType, 0),
Dirs: make(map[string]*dirDataType),
}
if !boxInfo.IsDir() {
return fmt.Errorf("Error: Box %s must point to a directory but points to %s instead\n",
boxname, boxPath)
}
// fill box datastructure with file data
err := filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("error walking box: %s\n", err)
}
filename := strings.TrimPrefix(path, boxPath)
filename = strings.Replace(filename, "\\", "/", -1)
filename = strings.TrimPrefix(filename, "/")
if info.IsDir() {
dirData := &dirDataType{
Identifier: "dir" + nextIdentifier(),
FileName: filename,
ModTime: info.ModTime().Unix(),
ChildFiles: make([]*fileDataType, 0),
ChildDirs: make([]*dirDataType, 0),
}
verbosef("\tincludes dir: '%s'\n", dirData.FileName)
box.Dirs[dirData.FileName] = dirData
// add tree entry (skip for root, it'll create a recursion)
if dirData.FileName != "" {
pathParts := strings.Split(dirData.FileName, "/")
parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
parentDir.ChildDirs = append(parentDir.ChildDirs, dirData)
}
} else {
fileData := &fileDataType{
Identifier: "file" + nextIdentifier(),
FileName: filename,
ModTime: info.ModTime().Unix(),
}
verbosef("\tincludes file: '%s'\n", fileData.FileName)
fileData.Content, err = ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("error reading file content while walking box: %s\n", err)
}
box.Files = append(box.Files, fileData)
// add tree entry
pathParts := strings.Split(fileData.FileName, "/")
parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
if parentDir == nil {
return fmt.Errorf("Error: parent of %s is not within the box\n", path)
}
parentDir.ChildFiles = append(parentDir.ChildFiles, fileData)
}
return nil
})
if err != nil {
return err
}
boxes = append(boxes, box)
}
embedSourceUnformated := bytes.NewBuffer(make([]byte, 0))
// execute template to buffer
err := tmplEmbeddedBox.Execute(
embedSourceUnformated,
embedFileDataType{pkg.Name, boxes},
)
if err != nil {
return fmt.Errorf("error writing embedded box to file (template execute): %s\n", err)
}
// format the source code
embedSource, err := format.Source(embedSourceUnformated.Bytes())
if err != nil {
return fmt.Errorf("error formatting embedSource: %s\n", err)
}
// write source to file
_, err = io.Copy(out, bytes.NewBuffer(embedSource))
if err != nil {
return fmt.Errorf("error writing embedSource to file: %s\n", err)
}
return nil
}
func operationEmbedGo(pkg *build.Package) {
// create go file for box
boxFile, err := os.Create(filepath.Join(pkg.Dir, boxFilename))
if err != nil {
log.Printf("error creating embedded box file: %s\n", err)
os.Exit(1)
}
defer boxFile.Close()
err = writeBoxesGo(pkg, boxFile)
if err != nil {
log.Printf("error creating embedded box file: %s\n", err)
os.Exit(1)
}
}

204
vendor/github.com/GeertJohan/go.rice/rice/embed-syso.go generated vendored Normal file
View File

@ -0,0 +1,204 @@
package main
import (
"bytes"
"encoding/gob"
"fmt"
"go/build"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"text/template"
"github.com/GeertJohan/go.rice/embedded"
"github.com/akavel/rsrc/coff"
)
type sizedReader struct {
*bytes.Reader
}
func (s sizedReader) Size() int64 {
return int64(s.Len())
}
var tmplEmbeddedSysoHelper *template.Template
func init() {
var err error
tmplEmbeddedSysoHelper, err = template.New("embeddedSysoHelper").Parse(`package {{.Package}}
// ############# GENERATED CODE #####################
// ## This file was generated by the rice tool.
// ## Do not edit unless you know what you're doing.
// ##################################################
// extern char _bricebox_{{.Symname}}[], _ericebox_{{.Symname}};
// int get_{{.Symname}}_length() {
// return &_ericebox_{{.Symname}} - _bricebox_{{.Symname}};
// }
import "C"
import (
"bytes"
"encoding/gob"
"github.com/GeertJohan/go.rice/embedded"
"unsafe"
)
func init() {
ptr := unsafe.Pointer(&C._bricebox_{{.Symname}})
bts := C.GoBytes(ptr, C.get_{{.Symname}}_length())
embeddedBox := &embedded.EmbeddedBox{}
err := gob.NewDecoder(bytes.NewReader(bts)).Decode(embeddedBox)
if err != nil {
panic("error decoding embedded box: "+err.Error())
}
embeddedBox.Link()
embedded.RegisterEmbeddedBox(embeddedBox.Name, embeddedBox)
}`)
if err != nil {
panic("could not parse template embeddedSysoHelper: " + err.Error())
}
}
type embeddedSysoHelperData struct {
Package string
Symname string
}
func operationEmbedSyso(pkg *build.Package) {
regexpSynameReplacer := regexp.MustCompile(`[^a-z0-9_]`)
boxMap := findBoxes(pkg)
// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
if len(boxMap) == 0 {
fmt.Println("no calls to rice.FindBox() found")
return
}
verbosef("\n")
for boxname := range boxMap {
// find path and filename for this box
boxPath := filepath.Join(pkg.Dir, boxname)
boxFilename := strings.Replace(boxname, "/", "-", -1)
boxFilename = strings.Replace(boxFilename, "..", "back", -1)
boxFilename = strings.Replace(boxFilename, ".", "-", -1)
// verbose info
verbosef("embedding box '%s'\n", boxname)
verbosef("\tto file %s\n", boxFilename)
// read box metadata
boxInfo, ierr := os.Stat(boxPath)
if ierr != nil {
fmt.Printf("Error: unable to access box at %s\n", boxPath)
os.Exit(1)
}
// create box datastructure (used by template)
box := &embedded.EmbeddedBox{
Name: boxname,
Time: boxInfo.ModTime(),
EmbedType: embedded.EmbedTypeSyso,
Files: make(map[string]*embedded.EmbeddedFile),
Dirs: make(map[string]*embedded.EmbeddedDir),
}
// fill box datastructure with file data
filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("error walking box: %s\n", err)
os.Exit(1)
}
filename := strings.TrimPrefix(path, boxPath)
filename = strings.Replace(filename, "\\", "/", -1)
filename = strings.TrimPrefix(filename, "/")
if info.IsDir() {
embeddedDir := &embedded.EmbeddedDir{
Filename: filename,
DirModTime: info.ModTime(),
}
verbosef("\tincludes dir: '%s'\n", embeddedDir.Filename)
box.Dirs[embeddedDir.Filename] = embeddedDir
// add tree entry (skip for root, it'll create a recursion)
if embeddedDir.Filename != "" {
pathParts := strings.Split(embeddedDir.Filename, "/")
parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
parentDir.ChildDirs = append(parentDir.ChildDirs, embeddedDir)
}
} else {
embeddedFile := &embedded.EmbeddedFile{
Filename: filename,
FileModTime: info.ModTime(),
Content: "",
}
verbosef("\tincludes file: '%s'\n", embeddedFile.Filename)
contentBytes, err := ioutil.ReadFile(path)
if err != nil {
fmt.Printf("error reading file content while walking box: %s\n", err)
os.Exit(1)
}
embeddedFile.Content = string(contentBytes)
box.Files[embeddedFile.Filename] = embeddedFile
}
return nil
})
// encode embedded box to gob file
boxGobBuf := &bytes.Buffer{}
err := gob.NewEncoder(boxGobBuf).Encode(box)
if err != nil {
fmt.Printf("error encoding box to gob: %v\n", err)
os.Exit(1)
}
verbosef("gob-encoded embeddedBox is %d bytes large\n", boxGobBuf.Len())
// write coff
symname := regexpSynameReplacer.ReplaceAllString(boxname, "_")
createCoffSyso(boxname, symname, "386", boxGobBuf.Bytes())
createCoffSyso(boxname, symname, "amd64", boxGobBuf.Bytes())
// write go
sysoHelperData := embeddedSysoHelperData{
Package: pkg.Name,
Symname: symname,
}
fileSysoHelper, err := os.Create(boxFilename + ".rice-box.go")
if err != nil {
fmt.Printf("error creating syso helper: %v\n", err)
os.Exit(1)
}
err = tmplEmbeddedSysoHelper.Execute(fileSysoHelper, sysoHelperData)
if err != nil {
fmt.Printf("error executing tmplEmbeddedSysoHelper: %v\n", err)
os.Exit(1)
}
}
}
func createCoffSyso(boxFilename string, symname string, arch string, data []byte) {
boxCoff := coff.NewRDATA()
switch arch {
case "386":
case "amd64":
boxCoff.FileHeader.Machine = 0x8664
default:
panic("invalid arch")
}
boxCoff.AddData("_bricebox_"+symname, sizedReader{bytes.NewReader(data)})
boxCoff.AddData("_ericebox_"+symname, io.NewSectionReader(strings.NewReader("\000\000"), 0, 2)) // TODO: why? copied from rsrc, which copied it from as-generated
boxCoff.Freeze()
err := writeCoff(boxCoff, boxFilename+"_"+arch+".rice-box.syso")
if err != nil {
fmt.Printf("error writing %s coff/.syso: %v\n", arch, err)
os.Exit(1)
}
}

150
vendor/github.com/GeertJohan/go.rice/rice/find.go generated vendored Normal file
View File

@ -0,0 +1,150 @@
package main
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"os"
"path/filepath"
"strings"
)
func badArgument(fileset *token.FileSet, p token.Pos) {
pos := fileset.Position(p)
filename := pos.Filename
base, err := os.Getwd()
if err == nil {
rpath, perr := filepath.Rel(base, pos.Filename)
if perr == nil {
filename = rpath
}
}
msg := fmt.Sprintf("%s:%d: Error: found call to rice.FindBox, "+
"but argument must be a string literal.\n", filename, pos.Line)
fmt.Println(msg)
os.Exit(1)
}
func findBoxes(pkg *build.Package) map[string]bool {
// create map of boxes to embed
var boxMap = make(map[string]bool)
// create one list of files for this package
filenames := make([]string, 0, len(pkg.GoFiles)+len(pkg.CgoFiles))
filenames = append(filenames, pkg.GoFiles...)
filenames = append(filenames, pkg.CgoFiles...)
// loop over files, search for rice.FindBox(..) calls
for _, filename := range filenames {
// find full filepath
fullpath := filepath.Join(pkg.Dir, filename)
if strings.HasSuffix(filename, "rice-box.go") {
// Ignore *.rice-box.go files
verbosef("skipping file %q\n", fullpath)
continue
}
verbosef("scanning file %q\n", fullpath)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fullpath, nil, 0)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
var riceIsImported bool
ricePkgName := "rice"
for _, imp := range f.Imports {
if strings.HasSuffix(imp.Path.Value, "go.rice\"") {
if imp.Name != nil {
ricePkgName = imp.Name.Name
}
riceIsImported = true
break
}
}
if !riceIsImported {
// Rice wasn't imported, so we won't find a box.
continue
}
if ricePkgName == "_" {
// Rice pkg is unnamed, so we won't find a box.
continue
}
// Inspect AST, looking for calls to (Must)?FindBox.
// First parameter of the func must be a basic literal.
// Identifiers won't be resolved.
var nextIdentIsBoxFunc bool
var nextBasicLitParamIsBoxName bool
var boxCall token.Pos
var variableToRemember string
var validVariablesForBoxes map[string]bool = make(map[string]bool)
ast.Inspect(f, func(node ast.Node) bool {
if node == nil {
return false
}
switch x := node.(type) {
// this case fixes the var := func() style assignments, not assignments to vars declared separately from the assignment.
case *ast.AssignStmt:
var assign = node.(*ast.AssignStmt)
name, found := assign.Lhs[0].(*ast.Ident)
if found {
variableToRemember = name.Name
composite, first := assign.Rhs[0].(*ast.CompositeLit)
if first {
riceSelector, second := composite.Type.(*ast.SelectorExpr)
if second {
callCorrect := riceSelector.Sel.Name == "Config"
packageName, third := riceSelector.X.(*ast.Ident)
if third && callCorrect && packageName.Name == ricePkgName {
validVariablesForBoxes[name.Name] = true
verbosef("\tfound variable, saving to scan for boxes: %q\n", name.Name)
}
}
}
}
case *ast.Ident:
if nextIdentIsBoxFunc || ricePkgName == "." {
nextIdentIsBoxFunc = false
if x.Name == "FindBox" || x.Name == "MustFindBox" {
nextBasicLitParamIsBoxName = true
boxCall = x.Pos()
}
} else {
if x.Name == ricePkgName || validVariablesForBoxes[x.Name] {
nextIdentIsBoxFunc = true
}
}
case *ast.BasicLit:
if nextBasicLitParamIsBoxName {
if x.Kind == token.STRING {
nextBasicLitParamIsBoxName = false
// trim "" or ``
name := x.Value[1 : len(x.Value)-1]
boxMap[name] = true
verbosef("\tfound box %q\n", name)
} else {
badArgument(fset, boxCall)
}
}
default:
if nextIdentIsBoxFunc {
nextIdentIsBoxFunc = false
}
if nextBasicLitParamIsBoxName {
badArgument(fset, boxCall)
}
}
return true
})
}
return boxMap
}

80
vendor/github.com/GeertJohan/go.rice/rice/flags.go generated vendored Normal file
View File

@ -0,0 +1,80 @@
package main
import (
"fmt"
"go/build"
"os"
goflags "github.com/jessevdk/go-flags" // rename import to `goflags` (file scope) so we can use `var flags` (package scope)
)
// flags
var flags struct {
Verbose bool `long:"verbose" short:"v" description:"Show verbose debug information"`
ImportPaths []string `long:"import-path" short:"i" description:"Import path(s) to use. Using PWD when left empty. Specify multiple times for more import paths to append"`
Append struct {
Executable string `long:"exec" description:"Executable to append" required:"true"`
} `command:"append"`
EmbedGo struct{} `command:"embed-go" alias:"embed"`
EmbedSyso struct{} `command:"embed-syso"`
Clean struct{} `command:"clean"`
}
// flags parser
var flagsParser *goflags.Parser
// initFlags parses the given flags.
// when the user asks for help (-h or --help): the application exists with status 0
// when unexpected flags is given: the application exits with status 1
func parseArguments() {
// create flags parser in global var, for flagsParser.Active.Name (operation)
flagsParser = goflags.NewParser(&flags, goflags.Default)
// parse flags
args, err := flagsParser.Parse()
if err != nil {
// assert the err to be a flags.Error
flagError := err.(*goflags.Error)
if flagError.Type == goflags.ErrHelp {
// user asked for help on flags.
// program can exit successfully
os.Exit(0)
}
if flagError.Type == goflags.ErrUnknownFlag {
fmt.Println("Use --help to view available options.")
os.Exit(1)
}
if flagError.Type == goflags.ErrRequired {
os.Exit(1)
}
fmt.Printf("Error parsing flags: %s\n", err)
os.Exit(1)
}
// error on left-over arguments
if len(args) > 0 {
fmt.Printf("Unexpected arguments: %s\nUse --help to view available options.", args)
os.Exit(1)
}
// default ImportPath to pwd when not set
if len(flags.ImportPaths) == 0 {
pwd, err := os.Getwd()
if err != nil {
fmt.Printf("error getting pwd: %s\n", err)
os.Exit(1)
}
verbosef("using pwd as import path\n")
// find non-absolute path for this pwd
pkg, err := build.ImportDir(pwd, build.FindOnly)
if err != nil {
fmt.Printf("error using current directory as import path: %s\n", err)
os.Exit(1)
}
flags.ImportPaths = append(flags.ImportPaths, pkg.ImportPath)
verbosef("using import paths: %s\n", flags.ImportPaths)
return
}
}

View File

@ -0,0 +1,14 @@
package main
import (
"strconv"
"github.com/GeertJohan/go.incremental"
)
var identifierCount incremental.Uint64
func nextIdentifier() string {
num := identifierCount.Next()
return strconv.FormatUint(num, 36) // 0123456789abcdefghijklmnopqrstuvwxyz
}

68
vendor/github.com/GeertJohan/go.rice/rice/main.go generated vendored Normal file
View File

@ -0,0 +1,68 @@
package main
import (
"fmt"
"go/build"
"log"
"os"
)
func main() {
// parser arguments
parseArguments()
// find package for path
var pkgs []*build.Package
for _, importPath := range flags.ImportPaths {
pkg := pkgForPath(importPath)
pkgs = append(pkgs, pkg)
}
// switch on the operation to perform
switch flagsParser.Active.Name {
case "embed", "embed-go":
for _, pkg := range pkgs {
operationEmbedGo(pkg)
}
case "embed-syso":
log.Println("WARNING: embedding .syso is experimental..")
for _, pkg := range pkgs {
operationEmbedSyso(pkg)
}
case "append":
operationAppend(pkgs)
case "clean":
for _, pkg := range pkgs {
operationClean(pkg)
}
}
// all done
verbosef("\n")
verbosef("rice finished successfully\n")
}
// helper function to get *build.Package for given path
func pkgForPath(path string) *build.Package {
// get pwd for relative imports
pwd, err := os.Getwd()
if err != nil {
fmt.Printf("error getting pwd (required for relative imports): %s\n", err)
os.Exit(1)
}
// read full package information
pkg, err := build.Import(path, pwd, 0)
if err != nil {
fmt.Printf("error reading package: %s\n", err)
os.Exit(1)
}
return pkg
}
func verbosef(format string, stuff ...interface{}) {
if flags.Verbose {
log.Printf(format, stuff...)
}
}

98
vendor/github.com/GeertJohan/go.rice/rice/templates.go generated vendored Normal file
View File

@ -0,0 +1,98 @@
package main
import (
"fmt"
"os"
"text/template"
)
var tmplEmbeddedBox *template.Template
func init() {
var err error
// parse embedded box template
tmplEmbeddedBox, err = template.New("embeddedBox").Parse(`package {{.Package}}
import (
"github.com/GeertJohan/go.rice/embedded"
"time"
)
{{range .Boxes}}
func init() {
// define files
{{range .Files}}{{.Identifier}} := &embedded.EmbeddedFile{
Filename: {{.FileName | printf "%q"}},
FileModTime: time.Unix({{.ModTime}}, 0),
Content: string({{.Content | printf "%q"}}),
}
{{end}}
// define dirs
{{range .Dirs}}{{.Identifier}} := &embedded.EmbeddedDir{
Filename: {{.FileName | printf "%q"}},
DirModTime: time.Unix({{.ModTime}}, 0),
ChildFiles: []*embedded.EmbeddedFile{
{{range .ChildFiles}}{{.Identifier}}, // {{.FileName | printf "%q"}}
{{end}}
},
}
{{end}}
// link ChildDirs
{{range .Dirs}}{{.Identifier}}.ChildDirs = []*embedded.EmbeddedDir{
{{range .ChildDirs}}{{.Identifier}}, // {{.FileName | printf "%q"}}
{{end}}
}
{{end}}
// register embeddedBox
embedded.RegisterEmbeddedBox(` + "`" + `{{.BoxName}}` + "`" + `, &embedded.EmbeddedBox{
Name: ` + "`" + `{{.BoxName}}` + "`" + `,
Time: time.Unix({{.UnixNow}}, 0),
Dirs: map[string]*embedded.EmbeddedDir{
{{range .Dirs}}{{.FileName | printf "%q"}}: {{.Identifier}},
{{end}}
},
Files: map[string]*embedded.EmbeddedFile{
{{range .Files}}{{.FileName | printf "%q"}}: {{.Identifier}},
{{end}}
},
})
}
{{end}}`)
if err != nil {
fmt.Printf("error parsing embedded box template: %s\n", err)
os.Exit(-1)
}
}
type embedFileDataType struct {
Package string
Boxes []*boxDataType
}
type boxDataType struct {
BoxName string
UnixNow int64
Files []*fileDataType
Dirs map[string]*dirDataType
}
type fileDataType struct {
Identifier string
FileName string
Content []byte
ModTime int64
}
type dirDataType struct {
Identifier string
FileName string
Content []byte
ModTime int64
ChildDirs []*dirDataType
ChildFiles []*fileDataType
}

22
vendor/github.com/GeertJohan/go.rice/rice/util.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
package main
import (
"math/rand"
"time"
)
// randomString generates a pseudo-random alpha-numeric string with given length.
func randomString(length int) string {
rand.Seed(time.Now().UnixNano())
k := make([]rune, length)
for i := 0; i < length; i++ {
c := rand.Intn(35)
if c < 10 {
c += 48 // numbers (0-9) (0+48 == 48 == '0', 9+48 == 57 == '9')
} else {
c += 87 // lower case alphabets (a-z) (10+87 == 97 == 'a', 35+87 == 122 = 'z')
}
k[i] = rune(c)
}
return string(k)
}

42
vendor/github.com/GeertJohan/go.rice/rice/writecoff.go generated vendored Normal file
View File

@ -0,0 +1,42 @@
package main
import (
"fmt"
"os"
"reflect"
"github.com/akavel/rsrc/binutil"
"github.com/akavel/rsrc/coff"
)
// copied from github.com/akavel/rsrc
// LICENSE: MIT
// Copyright 2013-2014 The rsrc Authors. (https://github.com/akavel/rsrc/blob/master/AUTHORS)
func writeCoff(coff *coff.Coff, fnameout string) error {
out, err := os.Create(fnameout)
if err != nil {
return err
}
defer out.Close()
w := binutil.Writer{W: out}
// write the resulting file to disk
binutil.Walk(coff, func(v reflect.Value, path string) error {
if binutil.Plain(v.Kind()) {
w.WriteLE(v.Interface())
return nil
}
vv, ok := v.Interface().(binutil.SizedReader)
if ok {
w.WriteFromSized(vv)
return binutil.WALK_SKIP
}
return nil
})
if w.Err != nil {
return fmt.Errorf("Error writing output file: %s", w.Err)
}
return nil
}

19
vendor/github.com/GeertJohan/go.rice/sort.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
package rice
import "os"
// SortByName allows an array of os.FileInfo objects
// to be easily sorted by filename using sort.Sort(SortByName(array))
type SortByName []os.FileInfo
func (f SortByName) Len() int { return len(f) }
func (f SortByName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
func (f SortByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
// SortByModified allows an array of os.FileInfo objects
// to be easily sorted by modified date using sort.Sort(SortByModified(array))
type SortByModified []os.FileInfo
func (f SortByModified) Len() int { return len(f) }
func (f SortByModified) Less(i, j int) bool { return f[i].ModTime().Unix() > f[j].ModTime().Unix() }
func (f SortByModified) Swap(i, j int) { f[i], f[j] = f[j], f[i] }

252
vendor/github.com/GeertJohan/go.rice/virtual.go generated vendored Normal file
View File

@ -0,0 +1,252 @@
package rice
import (
"errors"
"io"
"os"
"path/filepath"
"sort"
"github.com/GeertJohan/go.rice/embedded"
)
//++ TODO: IDEA: merge virtualFile and virtualDir, this decreases work done by rice.File
// Error indicating some function is not implemented yet (but available to satisfy an interface)
var ErrNotImplemented = errors.New("not implemented yet")
// virtualFile is a 'stateful' virtual file.
// virtualFile wraps an *EmbeddedFile for a call to Box.Open() and virtualizes 'read cursor' (offset) and 'closing'.
// virtualFile is only internally visible and should be exposed through rice.File
type virtualFile struct {
*embedded.EmbeddedFile // the actual embedded file, embedded to obtain methods
offset int64 // read position on the virtual file
closed bool // closed when true
}
// create a new virtualFile for given EmbeddedFile
func newVirtualFile(ef *embedded.EmbeddedFile) *virtualFile {
vf := &virtualFile{
EmbeddedFile: ef,
offset: 0,
closed: false,
}
return vf
}
//++ TODO check for nil pointers in all these methods. When so: return os.PathError with Err: os.ErrInvalid
func (vf *virtualFile) close() error {
if vf.closed {
return &os.PathError{
Op: "close",
Path: vf.EmbeddedFile.Filename,
Err: errors.New("already closed"),
}
}
vf.EmbeddedFile = nil
vf.closed = true
return nil
}
func (vf *virtualFile) stat() (os.FileInfo, error) {
if vf.closed {
return nil, &os.PathError{
Op: "stat",
Path: vf.EmbeddedFile.Filename,
Err: errors.New("bad file descriptor"),
}
}
return (*embeddedFileInfo)(vf.EmbeddedFile), nil
}
func (vf *virtualFile) readdir(count int) ([]os.FileInfo, error) {
if vf.closed {
return nil, &os.PathError{
Op: "readdir",
Path: vf.EmbeddedFile.Filename,
Err: errors.New("bad file descriptor"),
}
}
//TODO: return proper error for a readdir() call on a file
return nil, ErrNotImplemented
}
func (vf *virtualFile) read(bts []byte) (int, error) {
if vf.closed {
return 0, &os.PathError{
Op: "read",
Path: vf.EmbeddedFile.Filename,
Err: errors.New("bad file descriptor"),
}
}
end := vf.offset + int64(len(bts))
if end >= int64(len(vf.Content)) {
// end of file, so return what we have + EOF
n := copy(bts, vf.Content[vf.offset:])
vf.offset = 0
return n, io.EOF
}
n := copy(bts, vf.Content[vf.offset:end])
vf.offset += int64(n)
return n, nil
}
func (vf *virtualFile) seek(offset int64, whence int) (int64, error) {
if vf.closed {
return 0, &os.PathError{
Op: "seek",
Path: vf.EmbeddedFile.Filename,
Err: errors.New("bad file descriptor"),
}
}
var e error
//++ TODO: check if this is correct implementation for seek
switch whence {
case os.SEEK_SET:
//++ check if new offset isn't out of bounds, set e when it is, then break out of switch
vf.offset = offset
case os.SEEK_CUR:
//++ check if new offset isn't out of bounds, set e when it is, then break out of switch
vf.offset += offset
case os.SEEK_END:
//++ check if new offset isn't out of bounds, set e when it is, then break out of switch
vf.offset = int64(len(vf.EmbeddedFile.Content)) - offset
}
if e != nil {
return 0, &os.PathError{
Op: "seek",
Path: vf.Filename,
Err: e,
}
}
return vf.offset, nil
}
// virtualDir is a 'stateful' virtual directory.
// virtualDir wraps an *EmbeddedDir for a call to Box.Open() and virtualizes 'closing'.
// virtualDir is only internally visible and should be exposed through rice.File
type virtualDir struct {
*embedded.EmbeddedDir
offset int // readdir position on the directory
closed bool
}
// create a new virtualDir for given EmbeddedDir
func newVirtualDir(ed *embedded.EmbeddedDir) *virtualDir {
vd := &virtualDir{
EmbeddedDir: ed,
offset: 0,
closed: false,
}
return vd
}
func (vd *virtualDir) close() error {
//++ TODO: needs sync mutex?
if vd.closed {
return &os.PathError{
Op: "close",
Path: vd.EmbeddedDir.Filename,
Err: errors.New("already closed"),
}
}
vd.closed = true
return nil
}
func (vd *virtualDir) stat() (os.FileInfo, error) {
if vd.closed {
return nil, &os.PathError{
Op: "stat",
Path: vd.EmbeddedDir.Filename,
Err: errors.New("bad file descriptor"),
}
}
return (*embeddedDirInfo)(vd.EmbeddedDir), nil
}
func (vd *virtualDir) readdir(n int) (fi []os.FileInfo, err error) {
if vd.closed {
return nil, &os.PathError{
Op: "readdir",
Path: vd.EmbeddedDir.Filename,
Err: errors.New("bad file descriptor"),
}
}
// Build up the array of our contents
var files []os.FileInfo
// Add the child directories
for _, child := range vd.ChildDirs {
child.Filename = filepath.Base(child.Filename)
files = append(files, (*embeddedDirInfo)(child))
}
// Add the child files
for _, child := range vd.ChildFiles {
child.Filename = filepath.Base(child.Filename)
files = append(files, (*embeddedFileInfo)(child))
}
// Sort it by filename (lexical order)
sort.Sort(SortByName(files))
// Return all contents if that's what is requested
if n <= 0 {
vd.offset = 0
return files, nil
}
// If user has requested past the end of our list
// return what we can and send an EOF
if vd.offset+n >= len(files) {
offset := vd.offset
vd.offset = 0
return files[offset:], io.EOF
}
offset := vd.offset
vd.offset += n
return files[offset : offset+n], nil
}
func (vd *virtualDir) read(bts []byte) (int, error) {
if vd.closed {
return 0, &os.PathError{
Op: "read",
Path: vd.EmbeddedDir.Filename,
Err: errors.New("bad file descriptor"),
}
}
return 0, &os.PathError{
Op: "read",
Path: vd.EmbeddedDir.Filename,
Err: errors.New("is a directory"),
}
}
func (vd *virtualDir) seek(offset int64, whence int) (int64, error) {
if vd.closed {
return 0, &os.PathError{
Op: "seek",
Path: vd.EmbeddedDir.Filename,
Err: errors.New("bad file descriptor"),
}
}
return 0, &os.PathError{
Op: "seek",
Path: vd.Filename,
Err: errors.New("is a directory"),
}
}

122
vendor/github.com/GeertJohan/go.rice/walk.go generated vendored Normal file
View File

@ -0,0 +1,122 @@
package rice
import (
"os"
"path/filepath"
"sort"
"strings"
)
// Walk is like filepath.Walk()
// Visit http://golang.org/pkg/path/filepath/#Walk for more information
func (b *Box) Walk(path string, walkFn filepath.WalkFunc) error {
pathFile, err := b.Open(path)
if err != nil {
return err
}
defer pathFile.Close()
pathInfo, err := pathFile.Stat()
if err != nil {
return err
}
if b.IsAppended() || b.IsEmbedded() {
return b.walk(path, pathInfo, walkFn)
}
// We don't have any embedded or appended box so use live filesystem mode
return filepath.Walk(b.absolutePath+string(os.PathSeparator)+path, func(path string, info os.FileInfo, err error) error {
// Strip out the box name from the returned paths
path = strings.TrimPrefix(path, b.absolutePath+string(os.PathSeparator))
return walkFn(path, info, err)
})
}
// walk recursively descends path.
// See walk() in $GOROOT/src/pkg/path/filepath/path.go
func (b *Box) walk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
err := walkFn(path, info, nil)
if err != nil {
if info.IsDir() && err == filepath.SkipDir {
return nil
}
return err
}
if !info.IsDir() {
return nil
}
names, err := b.readDirNames(path)
if err != nil {
return walkFn(path, info, err)
}
for _, name := range names {
filename := filepath.Join(path, name)
fileObject, err := b.Open(filename)
if err != nil {
return err
}
defer fileObject.Close()
fileInfo, err := fileObject.Stat()
if err != nil {
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
return err
}
} else {
err = b.walk(filename, fileInfo, walkFn)
if err != nil {
if !fileInfo.IsDir() || err != filepath.SkipDir {
return err
}
}
}
}
return nil
}
// readDirNames reads the directory named by path and returns a sorted list of directory entries.
// See readDirNames() in $GOROOT/pkg/path/filepath/path.go
func (b *Box) readDirNames(path string) ([]string, error) {
f, err := b.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
stat, err := f.Stat()
if err != nil {
return nil, err
}
if !stat.IsDir() {
return nil, nil
}
infos, err := f.Readdir(0)
if err != nil {
return nil, err
}
var names []string
for _, info := range infos {
names = append(names, info.Name())
}
sort.Strings(names)
return names, nil
}

21
vendor/github.com/daaku/go.zipexe/license generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright © 2012-2015 Carlos Castillo
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

5
vendor/github.com/daaku/go.zipexe/readme.md generated vendored Normal file
View File

@ -0,0 +1,5 @@
go.zipexe
=========
This module was taken as-is from https://github.com/cookieo9/resources-go.
Documentation: https://godoc.org/github.com/daaku/go.zipexe

142
vendor/github.com/daaku/go.zipexe/zipexe.go generated vendored Normal file
View File

@ -0,0 +1,142 @@
// Package zipexe attempts to open an executable binary file as a zip file.
package zipexe
import (
"archive/zip"
"debug/elf"
"debug/macho"
"debug/pe"
"errors"
"io"
"os"
)
// Opens a zip file by path.
func Open(path string) (*zip.Reader, error) {
_, rd, err := OpenCloser(path)
return rd, err
}
// OpenCloser is like Open but returns an additional Closer to avoid leaking open files.
func OpenCloser(path string) (io.Closer, *zip.Reader, error) {
file, err := os.Open(path)
if err != nil {
return nil, nil, err
}
finfo, err := file.Stat()
if err != nil {
return nil, nil, err
}
zr, err := NewReader(file, finfo.Size())
if err != nil {
return nil, nil, err
}
return file, zr, nil
}
// Open a zip file, specially handling various binaries that may have been
// augmented with zip data.
func NewReader(rda io.ReaderAt, size int64) (*zip.Reader, error) {
handlers := []func(io.ReaderAt, int64) (*zip.Reader, error){
zip.NewReader,
zipExeReaderMacho,
zipExeReaderElf,
zipExeReaderPe,
}
for _, handler := range handlers {
zfile, err := handler(rda, size)
if err == nil {
return zfile, nil
}
}
return nil, errors.New("Couldn't Open As Executable")
}
// zipExeReaderMacho treats the file as a Mach-O binary
// (Mac OS X / Darwin executable) and attempts to find a zip archive.
func zipExeReaderMacho(rda io.ReaderAt, size int64) (*zip.Reader, error) {
file, err := macho.NewFile(rda)
if err != nil {
return nil, err
}
var max int64
for _, load := range file.Loads {
seg, ok := load.(*macho.Segment)
if ok {
// Check if the segment contains a zip file
if zfile, err := zip.NewReader(seg, int64(seg.Filesz)); err == nil {
return zfile, nil
}
// Otherwise move end of file pointer
end := int64(seg.Offset + seg.Filesz)
if end > max {
max = end
}
}
}
// No zip file within binary, try appended to end
section := io.NewSectionReader(rda, max, size-max)
return zip.NewReader(section, section.Size())
}
// zipExeReaderPe treats the file as a Portable Exectuable binary
// (Windows executable) and attempts to find a zip archive.
func zipExeReaderPe(rda io.ReaderAt, size int64) (*zip.Reader, error) {
file, err := pe.NewFile(rda)
if err != nil {
return nil, err
}
var max int64
for _, sec := range file.Sections {
// Check if this section has a zip file
if zfile, err := zip.NewReader(sec, int64(sec.Size)); err == nil {
return zfile, nil
}
// Otherwise move end of file pointer
end := int64(sec.Offset + sec.Size)
if end > max {
max = end
}
}
// No zip file within binary, try appended to end
section := io.NewSectionReader(rda, max, size-max)
return zip.NewReader(section, section.Size())
}
// zipExeReaderElf treats the file as a ELF binary
// (linux/BSD/etc... executable) and attempts to find a zip archive.
func zipExeReaderElf(rda io.ReaderAt, size int64) (*zip.Reader, error) {
file, err := elf.NewFile(rda)
if err != nil {
return nil, err
}
var max int64
for _, sect := range file.Sections {
if sect.Type == elf.SHT_NOBITS {
continue
}
// Check if this section has a zip file
if zfile, err := zip.NewReader(sect, int64(sect.Size)); err == nil {
return zfile, nil
}
// Otherwise move end of file pointer
end := int64(sect.Offset + sect.Size)
if end > max {
max = end
}
}
// No zip file within binary, try appended to end
section := io.NewSectionReader(rda, max, size-max)
return zip.NewReader(section, section.Size())
}

27
vendor/github.com/kardianos/osext/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

21
vendor/github.com/kardianos/osext/README.md generated vendored Normal file
View File

@ -0,0 +1,21 @@
### Extensions to the "os" package.
[![GoDoc](https://godoc.org/github.com/kardianos/osext?status.svg)](https://godoc.org/github.com/kardianos/osext)
## Find the current Executable and ExecutableFolder.
As of go1.8 the Executable function may be found in `os`. The Executable function
in the std lib `os` package is used if available.
There is sometimes utility in finding the current executable file
that is running. This can be used for upgrading the current executable
or finding resources located relative to the executable file. Both
working directory and the os.Args[0] value are arbitrary and cannot
be relied on; os.Args[0] can be "faked".
Multi-platform and supports:
* Linux
* OS X
* Windows
* Plan 9
* BSDs.

33
vendor/github.com/kardianos/osext/osext.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Extensions to the standard "os" package.
package osext // import "github.com/kardianos/osext"
import "path/filepath"
var cx, ce = executableClean()
func executableClean() (string, error) {
p, err := executable()
return filepath.Clean(p), err
}
// Executable returns an absolute path that can be used to
// re-invoke the current program.
// It may not be valid after the current program exits.
func Executable() (string, error) {
return cx, ce
}
// Returns same path as Executable, returns just the folder
// path. Excludes the executable name and any trailing slash.
func ExecutableFolder() (string, error) {
p, err := Executable()
if err != nil {
return "", err
}
return filepath.Dir(p), nil
}

9
vendor/github.com/kardianos/osext/osext_go18.go generated vendored Normal file
View File

@ -0,0 +1,9 @@
//+build go1.8,!openbsd
package osext
import "os"
func executable() (string, error) {
return os.Executable()
}

22
vendor/github.com/kardianos/osext/osext_plan9.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//+build !go1.8
package osext
import (
"os"
"strconv"
"syscall"
)
func executable() (string, error) {
f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
if err != nil {
return "", err
}
defer f.Close()
return syscall.Fd2path(int(f.Fd()))
}

36
vendor/github.com/kardianos/osext/osext_procfs.go generated vendored Normal file
View File

@ -0,0 +1,36 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.8,android !go1.8,linux !go1.8,netbsd !go1.8,solaris !go1.8,dragonfly
package osext
import (
"errors"
"fmt"
"os"
"runtime"
"strings"
)
func executable() (string, error) {
switch runtime.GOOS {
case "linux", "android":
const deletedTag = " (deleted)"
execpath, err := os.Readlink("/proc/self/exe")
if err != nil {
return execpath, err
}
execpath = strings.TrimSuffix(execpath, deletedTag)
execpath = strings.TrimPrefix(execpath, deletedTag)
return execpath, nil
case "netbsd":
return os.Readlink("/proc/curproc/exe")
case "dragonfly":
return os.Readlink("/proc/curproc/file")
case "solaris":
return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid()))
}
return "", errors.New("ExecPath not implemented for " + runtime.GOOS)
}

126
vendor/github.com/kardianos/osext/osext_sysctl.go generated vendored Normal file
View File

@ -0,0 +1,126 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.8,darwin !go1.8,freebsd openbsd
package osext
import (
"os"
"os/exec"
"path/filepath"
"runtime"
"syscall"
"unsafe"
)
var initCwd, initCwdErr = os.Getwd()
func executable() (string, error) {
var mib [4]int32
switch runtime.GOOS {
case "freebsd":
mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
case "darwin":
mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
case "openbsd":
mib = [4]int32{1 /* CTL_KERN */, 55 /* KERN_PROC_ARGS */, int32(os.Getpid()), 1 /* KERN_PROC_ARGV */}
}
n := uintptr(0)
// Get length.
_, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
if errNum != 0 {
return "", errNum
}
if n == 0 { // This shouldn't happen.
return "", nil
}
buf := make([]byte, n)
_, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
if errNum != 0 {
return "", errNum
}
if n == 0 { // This shouldn't happen.
return "", nil
}
var execPath string
switch runtime.GOOS {
case "openbsd":
// buf now contains **argv, with pointers to each of the C-style
// NULL terminated arguments.
var args []string
argv := uintptr(unsafe.Pointer(&buf[0]))
Loop:
for {
argp := *(**[1 << 20]byte)(unsafe.Pointer(argv))
if argp == nil {
break
}
for i := 0; uintptr(i) < n; i++ {
// we don't want the full arguments list
if string(argp[i]) == " " {
break Loop
}
if argp[i] != 0 {
continue
}
args = append(args, string(argp[:i]))
n -= uintptr(i)
break
}
if n < unsafe.Sizeof(argv) {
break
}
argv += unsafe.Sizeof(argv)
n -= unsafe.Sizeof(argv)
}
execPath = args[0]
// There is no canonical way to get an executable path on
// OpenBSD, so check PATH in case we are called directly
if execPath[0] != '/' && execPath[0] != '.' {
execIsInPath, err := exec.LookPath(execPath)
if err == nil {
execPath = execIsInPath
}
}
default:
for i, v := range buf {
if v == 0 {
buf = buf[:i]
break
}
}
execPath = string(buf)
}
var err error
// execPath will not be empty due to above checks.
// Try to get the absolute path if the execPath is not rooted.
if execPath[0] != '/' {
execPath, err = getAbs(execPath)
if err != nil {
return execPath, err
}
}
// For darwin KERN_PROCARGS may return the path to a symlink rather than the
// actual executable.
if runtime.GOOS == "darwin" {
if execPath, err = filepath.EvalSymlinks(execPath); err != nil {
return execPath, err
}
}
return execPath, nil
}
func getAbs(execPath string) (string, error) {
if initCwdErr != nil {
return execPath, initCwdErr
}
// The execPath may begin with a "../" or a "./" so clean it first.
// Join the two paths, trailing and starting slashes undetermined, so use
// the generic Join function.
return filepath.Join(initCwd, filepath.Clean(execPath)), nil
}

36
vendor/github.com/kardianos/osext/osext_windows.go generated vendored Normal file
View File

@ -0,0 +1,36 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//+build !go1.8
package osext
import (
"syscall"
"unicode/utf16"
"unsafe"
)
var (
kernel = syscall.MustLoadDLL("kernel32.dll")
getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
)
// GetModuleFileName() with hModule = NULL
func executable() (exePath string, err error) {
return getModuleFileName()
}
func getModuleFileName() (string, error) {
var n uint32
b := make([]uint16, syscall.MAX_PATH)
size := uint32(len(b))
r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
n = uint32(r0)
if n == 0 {
return "", e1
}
return string(utf16.Decode(b[0:n])), nil
}