forked from mirror/go-ethereum
parent
f2ab351e8d
commit
19b2640e89
@ -1,27 +0,0 @@ |
|||||||
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. |
|
@ -1,14 +0,0 @@ |
|||||||
### Extensions to the "os" package. |
|
||||||
|
|
||||||
## Find the current Executable and ExecutableFolder. |
|
||||||
|
|
||||||
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. |
|
||||||
|
|
||||||
Multi-platform and supports: |
|
||||||
* Linux |
|
||||||
* OS X |
|
||||||
* Windows |
|
||||||
* Plan 9 |
|
||||||
* BSDs. |
|
@ -1,27 +0,0 @@ |
|||||||
// 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 "path/filepath" |
|
||||||
|
|
||||||
// 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) { |
|
||||||
p, err := executable() |
|
||||||
return filepath.Clean(p), err |
|
||||||
} |
|
||||||
|
|
||||||
// Returns same path as Executable, returns just the folder
|
|
||||||
// path. Excludes the executable name.
|
|
||||||
func ExecutableFolder() (string, error) { |
|
||||||
p, err := Executable() |
|
||||||
if err != nil { |
|
||||||
return "", err |
|
||||||
} |
|
||||||
folder, _ := filepath.Split(p) |
|
||||||
return folder, nil |
|
||||||
} |
|
@ -1,20 +0,0 @@ |
|||||||
// 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.
|
|
||||||
|
|
||||||
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())) |
|
||||||
} |
|
@ -1,28 +0,0 @@ |
|||||||
// 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 linux netbsd openbsd solaris dragonfly
|
|
||||||
|
|
||||||
package osext |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"fmt" |
|
||||||
"os" |
|
||||||
"runtime" |
|
||||||
) |
|
||||||
|
|
||||||
func executable() (string, error) { |
|
||||||
switch runtime.GOOS { |
|
||||||
case "linux": |
|
||||||
return os.Readlink("/proc/self/exe") |
|
||||||
case "netbsd": |
|
||||||
return os.Readlink("/proc/curproc/exe") |
|
||||||
case "openbsd", "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) |
|
||||||
} |
|
@ -1,79 +0,0 @@ |
|||||||
// 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 darwin freebsd
|
|
||||||
|
|
||||||
package osext |
|
||||||
|
|
||||||
import ( |
|
||||||
"os" |
|
||||||
"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} |
|
||||||
} |
|
||||||
|
|
||||||
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 |
|
||||||
} |
|
||||||
for i, v := range buf { |
|
||||||
if v == 0 { |
|
||||||
buf = buf[:i] |
|
||||||
break |
|
||||||
} |
|
||||||
} |
|
||||||
var err error |
|
||||||
execPath := string(buf) |
|
||||||
// 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 |
|
||||||
} |
|
@ -1,79 +0,0 @@ |
|||||||
// 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 darwin linux freebsd netbsd windows
|
|
||||||
|
|
||||||
package osext |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"os" |
|
||||||
oexec "os/exec" |
|
||||||
"path/filepath" |
|
||||||
"runtime" |
|
||||||
"testing" |
|
||||||
) |
|
||||||
|
|
||||||
const execPath_EnvVar = "OSTEST_OUTPUT_EXECPATH" |
|
||||||
|
|
||||||
func TestExecPath(t *testing.T) { |
|
||||||
ep, err := Executable() |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("ExecPath failed: %v", err) |
|
||||||
} |
|
||||||
// we want fn to be of the form "dir/prog"
|
|
||||||
dir := filepath.Dir(filepath.Dir(ep)) |
|
||||||
fn, err := filepath.Rel(dir, ep) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("filepath.Rel: %v", err) |
|
||||||
} |
|
||||||
cmd := &oexec.Cmd{} |
|
||||||
// make child start with a relative program path
|
|
||||||
cmd.Dir = dir |
|
||||||
cmd.Path = fn |
|
||||||
// forge argv[0] for child, so that we can verify we could correctly
|
|
||||||
// get real path of the executable without influenced by argv[0].
|
|
||||||
cmd.Args = []string{"-", "-test.run=XXXX"} |
|
||||||
cmd.Env = []string{fmt.Sprintf("%s=1", execPath_EnvVar)} |
|
||||||
out, err := cmd.CombinedOutput() |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("exec(self) failed: %v", err) |
|
||||||
} |
|
||||||
outs := string(out) |
|
||||||
if !filepath.IsAbs(outs) { |
|
||||||
t.Fatalf("Child returned %q, want an absolute path", out) |
|
||||||
} |
|
||||||
if !sameFile(outs, ep) { |
|
||||||
t.Fatalf("Child returned %q, not the same file as %q", out, ep) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func sameFile(fn1, fn2 string) bool { |
|
||||||
fi1, err := os.Stat(fn1) |
|
||||||
if err != nil { |
|
||||||
return false |
|
||||||
} |
|
||||||
fi2, err := os.Stat(fn2) |
|
||||||
if err != nil { |
|
||||||
return false |
|
||||||
} |
|
||||||
return os.SameFile(fi1, fi2) |
|
||||||
} |
|
||||||
|
|
||||||
func init() { |
|
||||||
if e := os.Getenv(execPath_EnvVar); e != "" { |
|
||||||
// first chdir to another path
|
|
||||||
dir := "/" |
|
||||||
if runtime.GOOS == "windows" { |
|
||||||
dir = filepath.VolumeName(".") |
|
||||||
} |
|
||||||
os.Chdir(dir) |
|
||||||
if ep, err := Executable(); err != nil { |
|
||||||
fmt.Fprint(os.Stderr, "ERROR: ", err) |
|
||||||
} else { |
|
||||||
fmt.Fprint(os.Stderr, ep) |
|
||||||
} |
|
||||||
os.Exit(0) |
|
||||||
} |
|
||||||
} |
|
@ -1,34 +0,0 @@ |
|||||||
// 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.
|
|
||||||
|
|
||||||
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 |
|
||||||
} |
|
@ -1,4 +0,0 @@ |
|||||||
language: go |
|
||||||
go: |
|
||||||
- 1.3 |
|
||||||
- 1.4 |
|
@ -1,19 +0,0 @@ |
|||||||
Copyright (c) 2014 Olivier Poitrey <rs@dailymotion.com> |
|
||||||
|
|
||||||
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. |
|
@ -1,84 +0,0 @@ |
|||||||
# Go CORS handler [![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/cors) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/cors/master/LICENSE) [![build](https://img.shields.io/travis/rs/cors.svg?style=flat)](https://travis-ci.org/rs/cors) |
|
||||||
|
|
||||||
CORS is a `net/http` handler implementing [Cross Origin Resource Sharing W3 specification](http://www.w3.org/TR/cors/) in Golang. |
|
||||||
|
|
||||||
## Getting Started |
|
||||||
|
|
||||||
After installing Go and setting up your [GOPATH](http://golang.org/doc/code.html#GOPATH), create your first `.go` file. We'll call it `server.go`. |
|
||||||
|
|
||||||
```go |
|
||||||
package main |
|
||||||
|
|
||||||
import ( |
|
||||||
"net/http" |
|
||||||
|
|
||||||
"github.com/rs/cors" |
|
||||||
) |
|
||||||
|
|
||||||
func main() { |
|
||||||
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
||||||
w.Header().Set("Content-Type", "application/json") |
|
||||||
w.Write([]byte("{\"hello\": \"world\"}")) |
|
||||||
}) |
|
||||||
|
|
||||||
// cors.Default() setup the middleware with default options being |
|
||||||
// all origins accepted with simple methods (GET, POST). See |
|
||||||
// documentation below for more options. |
|
||||||
handler = cors.Default().Handler(h) |
|
||||||
http.ListenAndServe(":8080", handler) |
|
||||||
} |
|
||||||
``` |
|
||||||
|
|
||||||
Install `cors`: |
|
||||||
|
|
||||||
go get github.com/rs/cors |
|
||||||
|
|
||||||
Then run your server: |
|
||||||
|
|
||||||
go run server.go |
|
||||||
|
|
||||||
The server now runs on `localhost:8080`: |
|
||||||
|
|
||||||
$ curl -D - -H 'Origin: http://foo.com' http://localhost:8080/ |
|
||||||
HTTP/1.1 200 OK |
|
||||||
Access-Control-Allow-Origin: foo.com |
|
||||||
Content-Type: application/json |
|
||||||
Date: Sat, 25 Oct 2014 03:43:57 GMT |
|
||||||
Content-Length: 18 |
|
||||||
|
|
||||||
{"hello": "world"} |
|
||||||
|
|
||||||
### More Examples |
|
||||||
|
|
||||||
* `net/http`: [examples/nethttp/server.go](https://github.com/rs/cors/blob/master/examples/nethttp/server.go) |
|
||||||
* [Goji](https://goji.io): [examples/goji/server.go](https://github.com/rs/cors/blob/master/examples/goji/server.go) |
|
||||||
* [Martini](http://martini.codegangsta.io): [examples/martini/server.go](https://github.com/rs/cors/blob/master/examples/martini/server.go) |
|
||||||
* [Negroni](https://github.com/codegangsta/negroni): [examples/negroni/server.go](https://github.com/rs/cors/blob/master/examples/negroni/server.go) |
|
||||||
* [Alice](https://github.com/justinas/alice): [examples/alice/server.go](https://github.com/rs/cors/blob/master/examples/alice/server.go) |
|
||||||
|
|
||||||
## Parameters |
|
||||||
|
|
||||||
Parameters are passed to the middleware thru the `cors.New` method as follow: |
|
||||||
|
|
||||||
```go |
|
||||||
c := cors.New(cors.Options{ |
|
||||||
AllowedOrigins: []string{"http://foo.com"}, |
|
||||||
AllowCredentials: true, |
|
||||||
}) |
|
||||||
|
|
||||||
// Insert the middleware |
|
||||||
handler = c.Handler(handler) |
|
||||||
``` |
|
||||||
|
|
||||||
* **AllowedOrigins** `[]string`: A list of origins a cross-domain request can be executed from. If the special `*` value is present in the list, all origins will be allowed. The default value is `*`. |
|
||||||
* **AllowedMethods** `[]string`: A list of methods the client is allowed to use with cross-domain requests. |
|
||||||
* **AllowedHeaders** `[]string`: A list of non simple headers the client is allowed to use with cross-domain requests. Default value is simple methods (`GET` and `POST`) |
|
||||||
* **ExposedHeaders** `[]string`: Indicates which headers are safe to expose to the API of a CORS API specification |
|
||||||
* **AllowCredentials** `bool`: Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates. The default is `false`. |
|
||||||
* **MaxAge** `int`: Indicates how long (in seconds) the results of a preflight request can be cached. The default is `0` which stands for no max age. |
|
||||||
|
|
||||||
See [API documentation](http://godoc.org/github.com/rs/cors) for more info. |
|
||||||
|
|
||||||
## Licenses |
|
||||||
|
|
||||||
All source code is licensed under the [MIT License](https://raw.github.com/rs/cors/master/LICENSE). |
|
@ -1,37 +0,0 @@ |
|||||||
package cors |
|
||||||
|
|
||||||
import ( |
|
||||||
"net/http" |
|
||||||
"net/http/httptest" |
|
||||||
"testing" |
|
||||||
) |
|
||||||
|
|
||||||
func BenchmarkWithout(b *testing.B) { |
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("GET", "http://example.com/foo", nil) |
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
testHandler.ServeHTTP(res, req) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func BenchmarkDefault(b *testing.B) { |
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("GET", "http://example.com/foo", nil) |
|
||||||
handler := Default() |
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
handler.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func BenchmarkPreflight(b *testing.B) { |
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) |
|
||||||
req.Header.Add("Access-Control-Request-Method", "GET") |
|
||||||
handler := Default() |
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
handler.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
} |
|
||||||
} |
|
@ -1,308 +0,0 @@ |
|||||||
/* |
|
||||||
Package cors is net/http handler to handle CORS related requests |
|
||||||
as defined by http://www.w3.org/TR/cors/
|
|
||||||
|
|
||||||
You can configure it by passing an option struct to cors.New: |
|
||||||
|
|
||||||
c := cors.New(cors.Options{ |
|
||||||
AllowedOrigins: []string{"foo.com"}, |
|
||||||
AllowedMethods: []string{"GET", "POST", "DELETE"}, |
|
||||||
AllowCredentials: true, |
|
||||||
}) |
|
||||||
|
|
||||||
Then insert the handler in the chain: |
|
||||||
|
|
||||||
handler = c.Handler(handler) |
|
||||||
|
|
||||||
See Options documentation for more options. |
|
||||||
|
|
||||||
The resulting handler is a standard net/http handler. |
|
||||||
*/ |
|
||||||
package cors |
|
||||||
|
|
||||||
import ( |
|
||||||
"log" |
|
||||||
"net/http" |
|
||||||
"os" |
|
||||||
"strconv" |
|
||||||
"strings" |
|
||||||
) |
|
||||||
|
|
||||||
// Options is a configuration container to setup the CORS middleware.
|
|
||||||
type Options struct { |
|
||||||
// AllowedOrigins is a list of origins a cross-domain request can be executed from.
|
|
||||||
// If the special "*" value is present in the list, all origins will be allowed.
|
|
||||||
// Default value is ["*"]
|
|
||||||
AllowedOrigins []string |
|
||||||
// AllowedMethods is a list of methods the client is allowed to use with
|
|
||||||
// cross-domain requests. Default value is simple methods (GET and POST)
|
|
||||||
AllowedMethods []string |
|
||||||
// AllowedHeaders is list of non simple headers the client is allowed to use with
|
|
||||||
// cross-domain requests.
|
|
||||||
// If the special "*" value is present in the list, all headers will be allowed.
|
|
||||||
// Default value is [] but "Origin" is always appended to the list.
|
|
||||||
AllowedHeaders []string |
|
||||||
// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
|
|
||||||
// API specification
|
|
||||||
ExposedHeaders []string |
|
||||||
// AllowCredentials indicates whether the request can include user credentials like
|
|
||||||
// cookies, HTTP authentication or client side SSL certificates.
|
|
||||||
AllowCredentials bool |
|
||||||
// MaxAge indicates how long (in seconds) the results of a preflight request
|
|
||||||
// can be cached
|
|
||||||
MaxAge int |
|
||||||
// Debugging flag adds additional output to debug server side CORS issues
|
|
||||||
Debug bool |
|
||||||
// log object to use when debugging
|
|
||||||
log *log.Logger |
|
||||||
} |
|
||||||
|
|
||||||
type Cors struct { |
|
||||||
// The CORS Options
|
|
||||||
options Options |
|
||||||
} |
|
||||||
|
|
||||||
// New creates a new Cors handler with the provided options.
|
|
||||||
func New(options Options) *Cors { |
|
||||||
// Normalize options
|
|
||||||
// Note: for origins and methods matching, the spec requires a case-sensitive matching.
|
|
||||||
// As it may error prone, we chose to ignore the spec here.
|
|
||||||
normOptions := Options{ |
|
||||||
AllowedOrigins: convert(options.AllowedOrigins, strings.ToLower), |
|
||||||
AllowedMethods: convert(options.AllowedMethods, strings.ToUpper), |
|
||||||
// Origin is always appended as some browsers will always request
|
|
||||||
// for this header at preflight
|
|
||||||
AllowedHeaders: convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey), |
|
||||||
ExposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey), |
|
||||||
AllowCredentials: options.AllowCredentials, |
|
||||||
MaxAge: options.MaxAge, |
|
||||||
Debug: options.Debug, |
|
||||||
log: log.New(os.Stdout, "[cors] ", log.LstdFlags), |
|
||||||
} |
|
||||||
if len(normOptions.AllowedOrigins) == 0 { |
|
||||||
// Default is all origins
|
|
||||||
normOptions.AllowedOrigins = []string{"*"} |
|
||||||
} |
|
||||||
if len(normOptions.AllowedHeaders) == 1 { |
|
||||||
// Add some sensible defaults
|
|
||||||
normOptions.AllowedHeaders = []string{"Origin", "Accept", "Content-Type"} |
|
||||||
} |
|
||||||
if len(normOptions.AllowedMethods) == 0 { |
|
||||||
// Default is simple methods
|
|
||||||
normOptions.AllowedMethods = []string{"GET", "POST"} |
|
||||||
} |
|
||||||
|
|
||||||
if normOptions.Debug { |
|
||||||
normOptions.log.Printf("Options: %v", normOptions) |
|
||||||
} |
|
||||||
return &Cors{ |
|
||||||
options: normOptions, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Default creates a new Cors handler with default options
|
|
||||||
func Default() *Cors { |
|
||||||
return New(Options{}) |
|
||||||
} |
|
||||||
|
|
||||||
// Handler apply the CORS specification on the request, and add relevant CORS headers
|
|
||||||
// as necessary.
|
|
||||||
func (cors *Cors) Handler(h http.Handler) http.Handler { |
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
||||||
if r.Method == "OPTIONS" { |
|
||||||
cors.logf("Handler: Preflight request") |
|
||||||
cors.handlePreflight(w, r) |
|
||||||
// Preflight requests are standalone and should stop the chain as some other
|
|
||||||
// middleware may not handle OPTIONS requests correctly. One typical example
|
|
||||||
// is authentication middleware ; OPTIONS requests won't carry authentication
|
|
||||||
// headers (see #1)
|
|
||||||
} else { |
|
||||||
cors.logf("Handler: Actual request") |
|
||||||
cors.handleActualRequest(w, r) |
|
||||||
h.ServeHTTP(w, r) |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
// Martini compatible handler
|
|
||||||
func (cors *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) { |
|
||||||
if r.Method == "OPTIONS" { |
|
||||||
cors.logf("HandlerFunc: Preflight request") |
|
||||||
cors.handlePreflight(w, r) |
|
||||||
} else { |
|
||||||
cors.logf("HandlerFunc: Actual request") |
|
||||||
cors.handleActualRequest(w, r) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Negroni compatible interface
|
|
||||||
func (cors *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { |
|
||||||
if r.Method == "OPTIONS" { |
|
||||||
cors.logf("ServeHTTP: Preflight request") |
|
||||||
cors.handlePreflight(w, r) |
|
||||||
// Preflight requests are standalone and should stop the chain as some other
|
|
||||||
// middleware may not handle OPTIONS requests correctly. One typical example
|
|
||||||
// is authentication middleware ; OPTIONS requests won't carry authentication
|
|
||||||
// headers (see #1)
|
|
||||||
} else { |
|
||||||
cors.logf("ServeHTTP: Actual request") |
|
||||||
cors.handleActualRequest(w, r) |
|
||||||
next(w, r) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// handlePreflight handles pre-flight CORS requests
|
|
||||||
func (cors *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { |
|
||||||
options := cors.options |
|
||||||
headers := w.Header() |
|
||||||
origin := r.Header.Get("Origin") |
|
||||||
|
|
||||||
if r.Method != "OPTIONS" { |
|
||||||
cors.logf(" Preflight aborted: %s!=OPTIONS", r.Method) |
|
||||||
return |
|
||||||
} |
|
||||||
if origin == "" { |
|
||||||
cors.logf(" Preflight aborted: empty origin") |
|
||||||
return |
|
||||||
} |
|
||||||
if !cors.isOriginAllowed(origin) { |
|
||||||
cors.logf(" Preflight aborted: origin '%s' not allowed", origin) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
reqMethod := r.Header.Get("Access-Control-Request-Method") |
|
||||||
if !cors.isMethodAllowed(reqMethod) { |
|
||||||
cors.logf(" Preflight aborted: method '%s' not allowed", reqMethod) |
|
||||||
return |
|
||||||
} |
|
||||||
reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers")) |
|
||||||
if !cors.areHeadersAllowed(reqHeaders) { |
|
||||||
cors.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders) |
|
||||||
return |
|
||||||
} |
|
||||||
headers.Set("Access-Control-Allow-Origin", origin) |
|
||||||
headers.Add("Vary", "Origin") |
|
||||||
// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
|
|
||||||
// by Access-Control-Request-Method (if supported) can be enough
|
|
||||||
headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod)) |
|
||||||
if len(reqHeaders) > 0 { |
|
||||||
|
|
||||||
// Spec says: Since the list of headers can be unbounded, simply returning supported headers
|
|
||||||
// from Access-Control-Request-Headers can be enough
|
|
||||||
headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", ")) |
|
||||||
} |
|
||||||
if options.AllowCredentials { |
|
||||||
headers.Set("Access-Control-Allow-Credentials", "true") |
|
||||||
} |
|
||||||
if options.MaxAge > 0 { |
|
||||||
headers.Set("Access-Control-Max-Age", strconv.Itoa(options.MaxAge)) |
|
||||||
} |
|
||||||
cors.logf(" Preflight response headers: %v", headers) |
|
||||||
} |
|
||||||
|
|
||||||
// handleActualRequest handles simple cross-origin requests, actual request or redirects
|
|
||||||
func (cors *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { |
|
||||||
options := cors.options |
|
||||||
headers := w.Header() |
|
||||||
origin := r.Header.Get("Origin") |
|
||||||
|
|
||||||
if r.Method == "OPTIONS" { |
|
||||||
cors.logf(" Actual request no headers added: method == %s", r.Method) |
|
||||||
return |
|
||||||
} |
|
||||||
if origin == "" { |
|
||||||
cors.logf(" Actual request no headers added: missing origin") |
|
||||||
return |
|
||||||
} |
|
||||||
if !cors.isOriginAllowed(origin) { |
|
||||||
cors.logf(" Actual request no headers added: origin '%s' not allowed", origin) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Note that spec does define a way to specifically disallow a simple method like GET or
|
|
||||||
// POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
|
|
||||||
// spec doesn't instruct to check the allowed methods for simple cross-origin requests.
|
|
||||||
// We think it's a nice feature to be able to have control on those methods though.
|
|
||||||
if !cors.isMethodAllowed(r.Method) { |
|
||||||
if cors.options.Debug { |
|
||||||
cors.logf(" Actual request no headers added: method '%s' not allowed", |
|
||||||
r.Method) |
|
||||||
} |
|
||||||
|
|
||||||
return |
|
||||||
} |
|
||||||
headers.Set("Access-Control-Allow-Origin", origin) |
|
||||||
headers.Add("Vary", "Origin") |
|
||||||
if len(options.ExposedHeaders) > 0 { |
|
||||||
headers.Set("Access-Control-Expose-Headers", strings.Join(options.ExposedHeaders, ", ")) |
|
||||||
} |
|
||||||
if options.AllowCredentials { |
|
||||||
headers.Set("Access-Control-Allow-Credentials", "true") |
|
||||||
} |
|
||||||
cors.logf(" Actual response added headers: %v", headers) |
|
||||||
} |
|
||||||
|
|
||||||
// convenience method. checks if debugging is turned on before printing
|
|
||||||
func (cors *Cors) logf(format string, a ...interface{}) { |
|
||||||
if cors.options.Debug { |
|
||||||
cors.options.log.Printf(format, a...) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
|
|
||||||
// on the endpoint
|
|
||||||
func (cors *Cors) isOriginAllowed(origin string) bool { |
|
||||||
allowedOrigins := cors.options.AllowedOrigins |
|
||||||
origin = strings.ToLower(origin) |
|
||||||
for _, allowedOrigin := range allowedOrigins { |
|
||||||
switch allowedOrigin { |
|
||||||
case "*": |
|
||||||
return true |
|
||||||
case origin: |
|
||||||
return true |
|
||||||
} |
|
||||||
} |
|
||||||
return false |
|
||||||
} |
|
||||||
|
|
||||||
// isMethodAllowed checks if a given method can be used as part of a cross-domain request
|
|
||||||
// on the endpoing
|
|
||||||
func (cors *Cors) isMethodAllowed(method string) bool { |
|
||||||
allowedMethods := cors.options.AllowedMethods |
|
||||||
if len(allowedMethods) == 0 { |
|
||||||
// If no method allowed, always return false, even for preflight request
|
|
||||||
return false |
|
||||||
} |
|
||||||
method = strings.ToUpper(method) |
|
||||||
if method == "OPTIONS" { |
|
||||||
// Always allow preflight requests
|
|
||||||
return true |
|
||||||
} |
|
||||||
for _, allowedMethod := range allowedMethods { |
|
||||||
if allowedMethod == method { |
|
||||||
return true |
|
||||||
} |
|
||||||
} |
|
||||||
return false |
|
||||||
} |
|
||||||
|
|
||||||
// areHeadersAllowed checks if a given list of headers are allowed to used within
|
|
||||||
// a cross-domain request.
|
|
||||||
func (cors *Cors) areHeadersAllowed(requestedHeaders []string) bool { |
|
||||||
if len(requestedHeaders) == 0 { |
|
||||||
return true |
|
||||||
} |
|
||||||
for _, header := range requestedHeaders { |
|
||||||
found := false |
|
||||||
for _, allowedHeader := range cors.options.AllowedHeaders { |
|
||||||
if allowedHeader == "*" || allowedHeader == header { |
|
||||||
found = true |
|
||||||
break |
|
||||||
} |
|
||||||
} |
|
||||||
if !found { |
|
||||||
return false |
|
||||||
} |
|
||||||
} |
|
||||||
return true |
|
||||||
} |
|
@ -1,288 +0,0 @@ |
|||||||
package cors |
|
||||||
|
|
||||||
import ( |
|
||||||
"net/http" |
|
||||||
"net/http/httptest" |
|
||||||
"testing" |
|
||||||
) |
|
||||||
|
|
||||||
var testHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
||||||
w.Write([]byte("bar")) |
|
||||||
}) |
|
||||||
|
|
||||||
func assertHeaders(t *testing.T, resHeaders http.Header, reqHeaders map[string]string) { |
|
||||||
for name, value := range reqHeaders { |
|
||||||
if resHeaders.Get(name) != value { |
|
||||||
t.Errorf("Invalid header `%s', wanted `%s', got `%s'", name, value, resHeaders.Get(name)) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestNoConfig(t *testing.T) { |
|
||||||
s := New(Options{ |
|
||||||
// Intentionally left blank.
|
|
||||||
}) |
|
||||||
|
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("GET", "http://example.com/foo", nil) |
|
||||||
|
|
||||||
s.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
|
|
||||||
assertHeaders(t, res.Header(), map[string]string{ |
|
||||||
"Access-Control-Allow-Origin": "", |
|
||||||
"Access-Control-Allow-Methods": "", |
|
||||||
"Access-Control-Allow-Headers": "", |
|
||||||
"Access-Control-Allow-Credentials": "", |
|
||||||
"Access-Control-Max-Age": "", |
|
||||||
"Access-Control-Expose-Headers": "", |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TestWildcardOrigin(t *testing.T) { |
|
||||||
s := New(Options{ |
|
||||||
AllowedOrigins: []string{"*"}, |
|
||||||
}) |
|
||||||
|
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("GET", "http://example.com/foo", nil) |
|
||||||
req.Header.Add("Origin", "http://foobar.com") |
|
||||||
|
|
||||||
s.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
|
|
||||||
assertHeaders(t, res.Header(), map[string]string{ |
|
||||||
"Access-Control-Allow-Origin": "http://foobar.com", |
|
||||||
"Access-Control-Allow-Methods": "", |
|
||||||
"Access-Control-Allow-Headers": "", |
|
||||||
"Access-Control-Allow-Credentials": "", |
|
||||||
"Access-Control-Max-Age": "", |
|
||||||
"Access-Control-Expose-Headers": "", |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TestAllowedOrigin(t *testing.T) { |
|
||||||
s := New(Options{ |
|
||||||
AllowedOrigins: []string{"http://foobar.com"}, |
|
||||||
}) |
|
||||||
|
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("GET", "http://example.com/foo", nil) |
|
||||||
req.Header.Add("Origin", "http://foobar.com") |
|
||||||
|
|
||||||
s.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
|
|
||||||
assertHeaders(t, res.Header(), map[string]string{ |
|
||||||
"Access-Control-Allow-Origin": "http://foobar.com", |
|
||||||
"Access-Control-Allow-Methods": "", |
|
||||||
"Access-Control-Allow-Headers": "", |
|
||||||
"Access-Control-Allow-Credentials": "", |
|
||||||
"Access-Control-Max-Age": "", |
|
||||||
"Access-Control-Expose-Headers": "", |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TestDisallowedOrigin(t *testing.T) { |
|
||||||
s := New(Options{ |
|
||||||
AllowedOrigins: []string{"http://foobar.com"}, |
|
||||||
}) |
|
||||||
|
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("GET", "http://example.com/foo", nil) |
|
||||||
req.Header.Add("Origin", "http://barbaz.com") |
|
||||||
|
|
||||||
s.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
|
|
||||||
assertHeaders(t, res.Header(), map[string]string{ |
|
||||||
"Access-Control-Allow-Origin": "", |
|
||||||
"Access-Control-Allow-Methods": "", |
|
||||||
"Access-Control-Allow-Headers": "", |
|
||||||
"Access-Control-Allow-Credentials": "", |
|
||||||
"Access-Control-Max-Age": "", |
|
||||||
"Access-Control-Expose-Headers": "", |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TestAllowedMethod(t *testing.T) { |
|
||||||
s := New(Options{ |
|
||||||
AllowedOrigins: []string{"http://foobar.com"}, |
|
||||||
AllowedMethods: []string{"PUT", "DELETE"}, |
|
||||||
}) |
|
||||||
|
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) |
|
||||||
req.Header.Add("Origin", "http://foobar.com") |
|
||||||
req.Header.Add("Access-Control-Request-Method", "PUT") |
|
||||||
|
|
||||||
s.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
|
|
||||||
assertHeaders(t, res.Header(), map[string]string{ |
|
||||||
"Access-Control-Allow-Origin": "http://foobar.com", |
|
||||||
"Access-Control-Allow-Methods": "PUT", |
|
||||||
"Access-Control-Allow-Headers": "", |
|
||||||
"Access-Control-Allow-Credentials": "", |
|
||||||
"Access-Control-Max-Age": "", |
|
||||||
"Access-Control-Expose-Headers": "", |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TestDisallowedMethod(t *testing.T) { |
|
||||||
s := New(Options{ |
|
||||||
AllowedOrigins: []string{"http://foobar.com"}, |
|
||||||
AllowedMethods: []string{"PUT", "DELETE"}, |
|
||||||
}) |
|
||||||
|
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) |
|
||||||
req.Header.Add("Origin", "http://foobar.com") |
|
||||||
req.Header.Add("Access-Control-Request-Method", "PATCH") |
|
||||||
|
|
||||||
s.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
|
|
||||||
assertHeaders(t, res.Header(), map[string]string{ |
|
||||||
"Access-Control-Allow-Origin": "", |
|
||||||
"Access-Control-Allow-Methods": "", |
|
||||||
"Access-Control-Allow-Headers": "", |
|
||||||
"Access-Control-Allow-Credentials": "", |
|
||||||
"Access-Control-Max-Age": "", |
|
||||||
"Access-Control-Expose-Headers": "", |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TestAllowedHeader(t *testing.T) { |
|
||||||
s := New(Options{ |
|
||||||
AllowedOrigins: []string{"http://foobar.com"}, |
|
||||||
AllowedHeaders: []string{"X-Header-1", "x-header-2"}, |
|
||||||
}) |
|
||||||
|
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) |
|
||||||
req.Header.Add("Origin", "http://foobar.com") |
|
||||||
req.Header.Add("Access-Control-Request-Method", "GET") |
|
||||||
req.Header.Add("Access-Control-Request-Headers", "X-Header-2, X-HEADER-1") |
|
||||||
|
|
||||||
s.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
|
|
||||||
assertHeaders(t, res.Header(), map[string]string{ |
|
||||||
"Access-Control-Allow-Origin": "http://foobar.com", |
|
||||||
"Access-Control-Allow-Methods": "GET", |
|
||||||
"Access-Control-Allow-Headers": "X-Header-2, X-Header-1", |
|
||||||
"Access-Control-Allow-Credentials": "", |
|
||||||
"Access-Control-Max-Age": "", |
|
||||||
"Access-Control-Expose-Headers": "", |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TestAllowedWildcardHeader(t *testing.T) { |
|
||||||
s := New(Options{ |
|
||||||
AllowedOrigins: []string{"http://foobar.com"}, |
|
||||||
AllowedHeaders: []string{"*"}, |
|
||||||
}) |
|
||||||
|
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) |
|
||||||
req.Header.Add("Origin", "http://foobar.com") |
|
||||||
req.Header.Add("Access-Control-Request-Method", "GET") |
|
||||||
req.Header.Add("Access-Control-Request-Headers", "X-Header-2, X-HEADER-1") |
|
||||||
|
|
||||||
s.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
|
|
||||||
assertHeaders(t, res.Header(), map[string]string{ |
|
||||||
"Access-Control-Allow-Origin": "http://foobar.com", |
|
||||||
"Access-Control-Allow-Methods": "GET", |
|
||||||
"Access-Control-Allow-Headers": "X-Header-2, X-Header-1", |
|
||||||
"Access-Control-Allow-Credentials": "", |
|
||||||
"Access-Control-Max-Age": "", |
|
||||||
"Access-Control-Expose-Headers": "", |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TestDisallowedHeader(t *testing.T) { |
|
||||||
s := New(Options{ |
|
||||||
AllowedOrigins: []string{"http://foobar.com"}, |
|
||||||
AllowedHeaders: []string{"X-Header-1", "x-header-2"}, |
|
||||||
}) |
|
||||||
|
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) |
|
||||||
req.Header.Add("Origin", "http://foobar.com") |
|
||||||
req.Header.Add("Access-Control-Request-Method", "GET") |
|
||||||
req.Header.Add("Access-Control-Request-Headers", "X-Header-3, X-Header-1") |
|
||||||
|
|
||||||
s.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
|
|
||||||
assertHeaders(t, res.Header(), map[string]string{ |
|
||||||
"Access-Control-Allow-Origin": "", |
|
||||||
"Access-Control-Allow-Methods": "", |
|
||||||
"Access-Control-Allow-Headers": "", |
|
||||||
"Access-Control-Allow-Credentials": "", |
|
||||||
"Access-Control-Max-Age": "", |
|
||||||
"Access-Control-Expose-Headers": "", |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TestOriginHeader(t *testing.T) { |
|
||||||
s := New(Options{ |
|
||||||
AllowedOrigins: []string{"http://foobar.com"}, |
|
||||||
}) |
|
||||||
|
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) |
|
||||||
req.Header.Add("Origin", "http://foobar.com") |
|
||||||
req.Header.Add("Access-Control-Request-Method", "GET") |
|
||||||
req.Header.Add("Access-Control-Request-Headers", "origin") |
|
||||||
|
|
||||||
s.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
|
|
||||||
assertHeaders(t, res.Header(), map[string]string{ |
|
||||||
"Access-Control-Allow-Origin": "http://foobar.com", |
|
||||||
"Access-Control-Allow-Methods": "GET", |
|
||||||
"Access-Control-Allow-Headers": "Origin", |
|
||||||
"Access-Control-Allow-Credentials": "", |
|
||||||
"Access-Control-Max-Age": "", |
|
||||||
"Access-Control-Expose-Headers": "", |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TestExposedHeader(t *testing.T) { |
|
||||||
s := New(Options{ |
|
||||||
AllowedOrigins: []string{"http://foobar.com"}, |
|
||||||
ExposedHeaders: []string{"X-Header-1", "x-header-2"}, |
|
||||||
}) |
|
||||||
|
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("GET", "http://example.com/foo", nil) |
|
||||||
req.Header.Add("Origin", "http://foobar.com") |
|
||||||
|
|
||||||
s.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
|
|
||||||
assertHeaders(t, res.Header(), map[string]string{ |
|
||||||
"Access-Control-Allow-Origin": "http://foobar.com", |
|
||||||
"Access-Control-Allow-Methods": "", |
|
||||||
"Access-Control-Allow-Headers": "", |
|
||||||
"Access-Control-Allow-Credentials": "", |
|
||||||
"Access-Control-Max-Age": "", |
|
||||||
"Access-Control-Expose-Headers": "X-Header-1, X-Header-2", |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TestAllowedCredentials(t *testing.T) { |
|
||||||
s := New(Options{ |
|
||||||
AllowedOrigins: []string{"http://foobar.com"}, |
|
||||||
AllowCredentials: true, |
|
||||||
}) |
|
||||||
|
|
||||||
res := httptest.NewRecorder() |
|
||||||
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) |
|
||||||
req.Header.Add("Origin", "http://foobar.com") |
|
||||||
req.Header.Add("Access-Control-Request-Method", "GET") |
|
||||||
|
|
||||||
s.Handler(testHandler).ServeHTTP(res, req) |
|
||||||
|
|
||||||
assertHeaders(t, res.Header(), map[string]string{ |
|
||||||
"Access-Control-Allow-Origin": "http://foobar.com", |
|
||||||
"Access-Control-Allow-Methods": "GET", |
|
||||||
"Access-Control-Allow-Headers": "", |
|
||||||
"Access-Control-Allow-Credentials": "true", |
|
||||||
"Access-Control-Max-Age": "", |
|
||||||
"Access-Control-Expose-Headers": "", |
|
||||||
}) |
|
||||||
} |
|
@ -1,24 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
import ( |
|
||||||
"net/http" |
|
||||||
|
|
||||||
"github.com/justinas/alice" |
|
||||||
"github.com/rs/cors" |
|
||||||
) |
|
||||||
|
|
||||||
func main() { |
|
||||||
c := cors.New(cors.Options{ |
|
||||||
AllowedOrigins: []string{"http://foo.com"}, |
|
||||||
}) |
|
||||||
|
|
||||||
mux := http.NewServeMux() |
|
||||||
|
|
||||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
|
||||||
w.Header().Set("Content-Type", "application/json") |
|
||||||
w.Write([]byte("{\"hello\": \"world\"}")) |
|
||||||
}) |
|
||||||
|
|
||||||
chain := alice.New(c.Handler).Then(mux) |
|
||||||
http.ListenAndServe(":8080", chain) |
|
||||||
} |
|
@ -1,18 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
import ( |
|
||||||
"net/http" |
|
||||||
|
|
||||||
"github.com/rs/cors" |
|
||||||
) |
|
||||||
|
|
||||||
func main() { |
|
||||||
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
||||||
w.Header().Set("Content-Type", "application/json") |
|
||||||
w.Write([]byte("{\"hello\": \"world\"}")) |
|
||||||
}) |
|
||||||
|
|
||||||
// Use default options
|
|
||||||
handler := cors.Default().Handler(h) |
|
||||||
http.ListenAndServe(":8080", handler) |
|
||||||
} |
|
@ -1,22 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
import ( |
|
||||||
"net/http" |
|
||||||
|
|
||||||
"github.com/rs/cors" |
|
||||||
"github.com/zenazn/goji" |
|
||||||
) |
|
||||||
|
|
||||||
func main() { |
|
||||||
c := cors.New(cors.Options{ |
|
||||||
AllowedOrigins: []string{"http://foo.com"}, |
|
||||||
}) |
|
||||||
goji.Use(c.Handler) |
|
||||||
|
|
||||||
goji.Get("/", func(w http.ResponseWriter, r *http.Request) { |
|
||||||
w.Header().Set("Content-Type", "application/json") |
|
||||||
w.Write([]byte("{\"hello\": \"world\"}")) |
|
||||||
}) |
|
||||||
|
|
||||||
goji.Serve() |
|
||||||
} |
|
@ -1,23 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/go-martini/martini" |
|
||||||
"github.com/martini-contrib/render" |
|
||||||
"github.com/rs/cors" |
|
||||||
) |
|
||||||
|
|
||||||
func main() { |
|
||||||
c := cors.New(cors.Options{ |
|
||||||
AllowedOrigins: []string{"http://foo.com"}, |
|
||||||
}) |
|
||||||
|
|
||||||
m := martini.Classic() |
|
||||||
m.Use(render.Renderer()) |
|
||||||
m.Use(c.HandlerFunc) |
|
||||||
|
|
||||||
m.Get("/", func(r render.Render) { |
|
||||||
r.JSON(200, map[string]interface{}{"hello": "world"}) |
|
||||||
}) |
|
||||||
|
|
||||||
m.Run() |
|
||||||
} |
|
@ -1,26 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
import ( |
|
||||||
"net/http" |
|
||||||
|
|
||||||
"github.com/codegangsta/negroni" |
|
||||||
"github.com/rs/cors" |
|
||||||
) |
|
||||||
|
|
||||||
func main() { |
|
||||||
c := cors.New(cors.Options{ |
|
||||||
AllowedOrigins: []string{"http://foo.com"}, |
|
||||||
}) |
|
||||||
|
|
||||||
mux := http.NewServeMux() |
|
||||||
|
|
||||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
|
||||||
w.Header().Set("Content-Type", "application/json") |
|
||||||
w.Write([]byte("{\"hello\": \"world\"}")) |
|
||||||
}) |
|
||||||
|
|
||||||
n := negroni.Classic() |
|
||||||
n.Use(c) |
|
||||||
n.UseHandler(mux) |
|
||||||
n.Run(":3000") |
|
||||||
} |
|
@ -1,20 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
import ( |
|
||||||
"net/http" |
|
||||||
|
|
||||||
"github.com/rs/cors" |
|
||||||
) |
|
||||||
|
|
||||||
func main() { |
|
||||||
c := cors.New(cors.Options{ |
|
||||||
AllowedOrigins: []string{"http://foo.com"}, |
|
||||||
}) |
|
||||||
|
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
||||||
w.Header().Set("Content-Type", "application/json") |
|
||||||
w.Write([]byte("{\"hello\": \"world\"}")) |
|
||||||
}) |
|
||||||
|
|
||||||
http.ListenAndServe(":8080", c.Handler(handler)) |
|
||||||
} |
|
@ -1,22 +0,0 @@ |
|||||||
package main |
|
||||||
|
|
||||||
import ( |
|
||||||
"net/http" |
|
||||||
|
|
||||||
"github.com/rs/cors" |
|
||||||
) |
|
||||||
|
|
||||||
func main() { |
|
||||||
c := cors.New(cors.Options{ |
|
||||||
AllowedOrigins: []string{"*"}, |
|
||||||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"}, |
|
||||||
AllowCredentials: true, |
|
||||||
}) |
|
||||||
|
|
||||||
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
||||||
w.Header().Set("Content-Type", "application/json") |
|
||||||
w.Write([]byte("{\"hello\": \"world\"}")) |
|
||||||
}) |
|
||||||
|
|
||||||
http.ListenAndServe(":8080", c.Handler(h)) |
|
||||||
} |
|
@ -1,27 +0,0 @@ |
|||||||
package cors |
|
||||||
|
|
||||||
import ( |
|
||||||
"net/http" |
|
||||||
"strings" |
|
||||||
) |
|
||||||
|
|
||||||
type converter func(string) string |
|
||||||
|
|
||||||
// convert converts a list of string using the passed converter function
|
|
||||||
func convert(s []string, c converter) []string { |
|
||||||
out := []string{} |
|
||||||
for _, i := range s { |
|
||||||
out = append(out, c(i)) |
|
||||||
} |
|
||||||
return out |
|
||||||
} |
|
||||||
|
|
||||||
func parseHeaderList(headerList string) (headers []string) { |
|
||||||
for _, header := range strings.Split(headerList, ",") { |
|
||||||
header = http.CanonicalHeaderKey(strings.TrimSpace(header)) |
|
||||||
if header != "" { |
|
||||||
headers = append(headers, header) |
|
||||||
} |
|
||||||
} |
|
||||||
return headers |
|
||||||
} |
|
@ -1,28 +0,0 @@ |
|||||||
package cors |
|
||||||
|
|
||||||
import ( |
|
||||||
"strings" |
|
||||||
"testing" |
|
||||||
) |
|
||||||
|
|
||||||
func TestConvert(t *testing.T) { |
|
||||||
s := convert([]string{"A", "b", "C"}, strings.ToLower) |
|
||||||
e := []string{"a", "b", "c"} |
|
||||||
if s[0] != e[0] || s[1] != e[1] || s[2] != e[2] { |
|
||||||
t.Errorf("%v != %v", s, e) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestParseHeaderList(t *testing.T) { |
|
||||||
h := parseHeaderList("header, second-header, THIRD-HEADER") |
|
||||||
e := []string{"Header", "Second-Header", "Third-Header"} |
|
||||||
if h[0] != e[0] || h[1] != e[1] || h[2] != e[2] { |
|
||||||
t.Errorf("%v != %v", h, e) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestParseHeaderListEmpty(t *testing.T) { |
|
||||||
if len(parseHeaderList("")) != 0 { |
|
||||||
t.Error("should be empty sclice") |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,113 @@ |
|||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
package websocket |
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"crypto/tls" |
||||||
|
"io" |
||||||
|
"net" |
||||||
|
"net/http" |
||||||
|
"net/url" |
||||||
|
) |
||||||
|
|
||||||
|
// DialError is an error that occurs while dialling a websocket server.
|
||||||
|
type DialError struct { |
||||||
|
*Config |
||||||
|
Err error |
||||||
|
} |
||||||
|
|
||||||
|
func (e *DialError) Error() string { |
||||||
|
return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error() |
||||||
|
} |
||||||
|
|
||||||
|
// NewConfig creates a new WebSocket config for client connection.
|
||||||
|
func NewConfig(server, origin string) (config *Config, err error) { |
||||||
|
config = new(Config) |
||||||
|
config.Version = ProtocolVersionHybi13 |
||||||
|
config.Location, err = url.ParseRequestURI(server) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
config.Origin, err = url.ParseRequestURI(origin) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
config.Header = http.Header(make(map[string][]string)) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// NewClient creates a new WebSocket client connection over rwc.
|
||||||
|
func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) { |
||||||
|
br := bufio.NewReader(rwc) |
||||||
|
bw := bufio.NewWriter(rwc) |
||||||
|
err = hybiClientHandshake(config, br, bw) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
buf := bufio.NewReadWriter(br, bw) |
||||||
|
ws = newHybiClientConn(config, buf, rwc) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Dial opens a new client connection to a WebSocket.
|
||||||
|
func Dial(url_, protocol, origin string) (ws *Conn, err error) { |
||||||
|
config, err := NewConfig(url_, origin) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
if protocol != "" { |
||||||
|
config.Protocol = []string{protocol} |
||||||
|
} |
||||||
|
return DialConfig(config) |
||||||
|
} |
||||||
|
|
||||||
|
var portMap = map[string]string{ |
||||||
|
"ws": "80", |
||||||
|
"wss": "443", |
||||||
|
} |
||||||
|
|
||||||
|
func parseAuthority(location *url.URL) string { |
||||||
|
if _, ok := portMap[location.Scheme]; ok { |
||||||
|
if _, _, err := net.SplitHostPort(location.Host); err != nil { |
||||||
|
return net.JoinHostPort(location.Host, portMap[location.Scheme]) |
||||||
|
} |
||||||
|
} |
||||||
|
return location.Host |
||||||
|
} |
||||||
|
|
||||||
|
// DialConfig opens a new client connection to a WebSocket with a config.
|
||||||
|
func DialConfig(config *Config) (ws *Conn, err error) { |
||||||
|
var client net.Conn |
||||||
|
if config.Location == nil { |
||||||
|
return nil, &DialError{config, ErrBadWebSocketLocation} |
||||||
|
} |
||||||
|
if config.Origin == nil { |
||||||
|
return nil, &DialError{config, ErrBadWebSocketOrigin} |
||||||
|
} |
||||||
|
switch config.Location.Scheme { |
||||||
|
case "ws": |
||||||
|
client, err = net.Dial("tcp", parseAuthority(config.Location)) |
||||||
|
|
||||||
|
case "wss": |
||||||
|
client, err = tls.Dial("tcp", parseAuthority(config.Location), config.TlsConfig) |
||||||
|
|
||||||
|
default: |
||||||
|
err = ErrBadScheme |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
goto Error |
||||||
|
} |
||||||
|
|
||||||
|
ws, err = NewClient(config, client) |
||||||
|
if err != nil { |
||||||
|
client.Close() |
||||||
|
goto Error |
||||||
|
} |
||||||
|
return |
||||||
|
|
||||||
|
Error: |
||||||
|
return nil, &DialError{config, err} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package websocket_test |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"log" |
||||||
|
|
||||||
|
"golang.org/x/net/websocket" |
||||||
|
) |
||||||
|
|
||||||
|
// This example demonstrates a trivial client.
|
||||||
|
func ExampleDial() { |
||||||
|
origin := "http://localhost/" |
||||||
|
url := "ws://localhost:12345/ws" |
||||||
|
ws, err := websocket.Dial(url, "", origin) |
||||||
|
if err != nil { |
||||||
|
log.Fatal(err) |
||||||
|
} |
||||||
|
if _, err := ws.Write([]byte("hello, world!\n")); err != nil { |
||||||
|
log.Fatal(err) |
||||||
|
} |
||||||
|
var msg = make([]byte, 512) |
||||||
|
var n int |
||||||
|
if n, err = ws.Read(msg); err != nil { |
||||||
|
log.Fatal(err) |
||||||
|
} |
||||||
|
fmt.Printf("Received: %s.\n", msg[:n]) |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package websocket_test |
||||||
|
|
||||||
|
import ( |
||||||
|
"io" |
||||||
|
"net/http" |
||||||
|
|
||||||
|
"golang.org/x/net/websocket" |
||||||
|
) |
||||||
|
|
||||||
|
// Echo the data received on the WebSocket.
|
||||||
|
func EchoServer(ws *websocket.Conn) { |
||||||
|
io.Copy(ws, ws) |
||||||
|
} |
||||||
|
|
||||||
|
// This example demonstrates a trivial echo server.
|
||||||
|
func ExampleHandler() { |
||||||
|
http.Handle("/echo", websocket.Handler(EchoServer)) |
||||||
|
err := http.ListenAndServe(":12345", nil) |
||||||
|
if err != nil { |
||||||
|
panic("ListenAndServe: " + err.Error()) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,564 @@ |
|||||||
|
// Copyright 2011 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.
|
||||||
|
|
||||||
|
package websocket |
||||||
|
|
||||||
|
// This file implements a protocol of hybi draft.
|
||||||
|
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
|
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"bytes" |
||||||
|
"crypto/rand" |
||||||
|
"crypto/sha1" |
||||||
|
"encoding/base64" |
||||||
|
"encoding/binary" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"io/ioutil" |
||||||
|
"net/http" |
||||||
|
"net/url" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" |
||||||
|
|
||||||
|
closeStatusNormal = 1000 |
||||||
|
closeStatusGoingAway = 1001 |
||||||
|
closeStatusProtocolError = 1002 |
||||||
|
closeStatusUnsupportedData = 1003 |
||||||
|
closeStatusFrameTooLarge = 1004 |
||||||
|
closeStatusNoStatusRcvd = 1005 |
||||||
|
closeStatusAbnormalClosure = 1006 |
||||||
|
closeStatusBadMessageData = 1007 |
||||||
|
closeStatusPolicyViolation = 1008 |
||||||
|
closeStatusTooBigData = 1009 |
||||||
|
closeStatusExtensionMismatch = 1010 |
||||||
|
|
||||||
|
maxControlFramePayloadLength = 125 |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
ErrBadMaskingKey = &ProtocolError{"bad masking key"} |
||||||
|
ErrBadPongMessage = &ProtocolError{"bad pong message"} |
||||||
|
ErrBadClosingStatus = &ProtocolError{"bad closing status"} |
||||||
|
ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"} |
||||||
|
ErrNotImplemented = &ProtocolError{"not implemented"} |
||||||
|
|
||||||
|
handshakeHeader = map[string]bool{ |
||||||
|
"Host": true, |
||||||
|
"Upgrade": true, |
||||||
|
"Connection": true, |
||||||
|
"Sec-Websocket-Key": true, |
||||||
|
"Sec-Websocket-Origin": true, |
||||||
|
"Sec-Websocket-Version": true, |
||||||
|
"Sec-Websocket-Protocol": true, |
||||||
|
"Sec-Websocket-Accept": true, |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
// A hybiFrameHeader is a frame header as defined in hybi draft.
|
||||||
|
type hybiFrameHeader struct { |
||||||
|
Fin bool |
||||||
|
Rsv [3]bool |
||||||
|
OpCode byte |
||||||
|
Length int64 |
||||||
|
MaskingKey []byte |
||||||
|
|
||||||
|
data *bytes.Buffer |
||||||
|
} |
||||||
|
|
||||||
|
// A hybiFrameReader is a reader for hybi frame.
|
||||||
|
type hybiFrameReader struct { |
||||||
|
reader io.Reader |
||||||
|
|
||||||
|
header hybiFrameHeader |
||||||
|
pos int64 |
||||||
|
length int |
||||||
|
} |
||||||
|
|
||||||
|
func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) { |
||||||
|
n, err = frame.reader.Read(msg) |
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
if frame.header.MaskingKey != nil { |
||||||
|
for i := 0; i < n; i++ { |
||||||
|
msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4] |
||||||
|
frame.pos++ |
||||||
|
} |
||||||
|
} |
||||||
|
return n, err |
||||||
|
} |
||||||
|
|
||||||
|
func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode } |
||||||
|
|
||||||
|
func (frame *hybiFrameReader) HeaderReader() io.Reader { |
||||||
|
if frame.header.data == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
if frame.header.data.Len() == 0 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return frame.header.data |
||||||
|
} |
||||||
|
|
||||||
|
func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil } |
||||||
|
|
||||||
|
func (frame *hybiFrameReader) Len() (n int) { return frame.length } |
||||||
|
|
||||||
|
// A hybiFrameReaderFactory creates new frame reader based on its frame type.
|
||||||
|
type hybiFrameReaderFactory struct { |
||||||
|
*bufio.Reader |
||||||
|
} |
||||||
|
|
||||||
|
// NewFrameReader reads a frame header from the connection, and creates new reader for the frame.
|
||||||
|
// See Section 5.2 Base Framing protocol for detail.
|
||||||
|
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2
|
||||||
|
func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) { |
||||||
|
hybiFrame := new(hybiFrameReader) |
||||||
|
frame = hybiFrame |
||||||
|
var header []byte |
||||||
|
var b byte |
||||||
|
// First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits)
|
||||||
|
b, err = buf.ReadByte() |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
header = append(header, b) |
||||||
|
hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0 |
||||||
|
for i := 0; i < 3; i++ { |
||||||
|
j := uint(6 - i) |
||||||
|
hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0 |
||||||
|
} |
||||||
|
hybiFrame.header.OpCode = header[0] & 0x0f |
||||||
|
|
||||||
|
// Second byte. Mask/Payload len(7bits)
|
||||||
|
b, err = buf.ReadByte() |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
header = append(header, b) |
||||||
|
mask := (b & 0x80) != 0 |
||||||
|
b &= 0x7f |
||||||
|
lengthFields := 0 |
||||||
|
switch { |
||||||
|
case b <= 125: // Payload length 7bits.
|
||||||
|
hybiFrame.header.Length = int64(b) |
||||||
|
case b == 126: // Payload length 7+16bits
|
||||||
|
lengthFields = 2 |
||||||
|
case b == 127: // Payload length 7+64bits
|
||||||
|
lengthFields = 8 |
||||||
|
} |
||||||
|
for i := 0; i < lengthFields; i++ { |
||||||
|
b, err = buf.ReadByte() |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
header = append(header, b) |
||||||
|
hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b) |
||||||
|
} |
||||||
|
if mask { |
||||||
|
// Masking key. 4 bytes.
|
||||||
|
for i := 0; i < 4; i++ { |
||||||
|
b, err = buf.ReadByte() |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
header = append(header, b) |
||||||
|
hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b) |
||||||
|
} |
||||||
|
} |
||||||
|
hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length) |
||||||
|
hybiFrame.header.data = bytes.NewBuffer(header) |
||||||
|
hybiFrame.length = len(header) + int(hybiFrame.header.Length) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// A HybiFrameWriter is a writer for hybi frame.
|
||||||
|
type hybiFrameWriter struct { |
||||||
|
writer *bufio.Writer |
||||||
|
|
||||||
|
header *hybiFrameHeader |
||||||
|
} |
||||||
|
|
||||||
|
func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) { |
||||||
|
var header []byte |
||||||
|
var b byte |
||||||
|
if frame.header.Fin { |
||||||
|
b |= 0x80 |
||||||
|
} |
||||||
|
for i := 0; i < 3; i++ { |
||||||
|
if frame.header.Rsv[i] { |
||||||
|
j := uint(6 - i) |
||||||
|
b |= 1 << j |
||||||
|
} |
||||||
|
} |
||||||
|
b |= frame.header.OpCode |
||||||
|
header = append(header, b) |
||||||
|
if frame.header.MaskingKey != nil { |
||||||
|
b = 0x80 |
||||||
|
} else { |
||||||
|
b = 0 |
||||||
|
} |
||||||
|
lengthFields := 0 |
||||||
|
length := len(msg) |
||||||
|
switch { |
||||||
|
case length <= 125: |
||||||
|
b |= byte(length) |
||||||
|
case length < 65536: |
||||||
|
b |= 126 |
||||||
|
lengthFields = 2 |
||||||
|
default: |
||||||
|
b |= 127 |
||||||
|
lengthFields = 8 |
||||||
|
} |
||||||
|
header = append(header, b) |
||||||
|
for i := 0; i < lengthFields; i++ { |
||||||
|
j := uint((lengthFields - i - 1) * 8) |
||||||
|
b = byte((length >> j) & 0xff) |
||||||
|
header = append(header, b) |
||||||
|
} |
||||||
|
if frame.header.MaskingKey != nil { |
||||||
|
if len(frame.header.MaskingKey) != 4 { |
||||||
|
return 0, ErrBadMaskingKey |
||||||
|
} |
||||||
|
header = append(header, frame.header.MaskingKey...) |
||||||
|
frame.writer.Write(header) |
||||||
|
data := make([]byte, length) |
||||||
|
for i := range data { |
||||||
|
data[i] = msg[i] ^ frame.header.MaskingKey[i%4] |
||||||
|
} |
||||||
|
frame.writer.Write(data) |
||||||
|
err = frame.writer.Flush() |
||||||
|
return length, err |
||||||
|
} |
||||||
|
frame.writer.Write(header) |
||||||
|
frame.writer.Write(msg) |
||||||
|
err = frame.writer.Flush() |
||||||
|
return length, err |
||||||
|
} |
||||||
|
|
||||||
|
func (frame *hybiFrameWriter) Close() error { return nil } |
||||||
|
|
||||||
|
type hybiFrameWriterFactory struct { |
||||||
|
*bufio.Writer |
||||||
|
needMaskingKey bool |
||||||
|
} |
||||||
|
|
||||||
|
func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) { |
||||||
|
frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType} |
||||||
|
if buf.needMaskingKey { |
||||||
|
frameHeader.MaskingKey, err = generateMaskingKey() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil |
||||||
|
} |
||||||
|
|
||||||
|
type hybiFrameHandler struct { |
||||||
|
conn *Conn |
||||||
|
payloadType byte |
||||||
|
} |
||||||
|
|
||||||
|
func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err error) { |
||||||
|
if handler.conn.IsServerConn() { |
||||||
|
// The client MUST mask all frames sent to the server.
|
||||||
|
if frame.(*hybiFrameReader).header.MaskingKey == nil { |
||||||
|
handler.WriteClose(closeStatusProtocolError) |
||||||
|
return nil, io.EOF |
||||||
|
} |
||||||
|
} else { |
||||||
|
// The server MUST NOT mask all frames.
|
||||||
|
if frame.(*hybiFrameReader).header.MaskingKey != nil { |
||||||
|
handler.WriteClose(closeStatusProtocolError) |
||||||
|
return nil, io.EOF |
||||||
|
} |
||||||
|
} |
||||||
|
if header := frame.HeaderReader(); header != nil { |
||||||
|
io.Copy(ioutil.Discard, header) |
||||||
|
} |
||||||
|
switch frame.PayloadType() { |
||||||
|
case ContinuationFrame: |
||||||
|
frame.(*hybiFrameReader).header.OpCode = handler.payloadType |
||||||
|
case TextFrame, BinaryFrame: |
||||||
|
handler.payloadType = frame.PayloadType() |
||||||
|
case CloseFrame: |
||||||
|
return nil, io.EOF |
||||||
|
case PingFrame: |
||||||
|
pingMsg := make([]byte, maxControlFramePayloadLength) |
||||||
|
n, err := io.ReadFull(frame, pingMsg) |
||||||
|
if err != nil && err != io.ErrUnexpectedEOF { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
io.Copy(ioutil.Discard, frame) |
||||||
|
n, err = handler.WritePong(pingMsg[:n]) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return nil, nil |
||||||
|
case PongFrame: |
||||||
|
return nil, ErrNotImplemented |
||||||
|
} |
||||||
|
return frame, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (handler *hybiFrameHandler) WriteClose(status int) (err error) { |
||||||
|
handler.conn.wio.Lock() |
||||||
|
defer handler.conn.wio.Unlock() |
||||||
|
w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
msg := make([]byte, 2) |
||||||
|
binary.BigEndian.PutUint16(msg, uint16(status)) |
||||||
|
_, err = w.Write(msg) |
||||||
|
w.Close() |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) { |
||||||
|
handler.conn.wio.Lock() |
||||||
|
defer handler.conn.wio.Unlock() |
||||||
|
w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame) |
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
n, err = w.Write(msg) |
||||||
|
w.Close() |
||||||
|
return n, err |
||||||
|
} |
||||||
|
|
||||||
|
// newHybiConn creates a new WebSocket connection speaking hybi draft protocol.
|
||||||
|
func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { |
||||||
|
if buf == nil { |
||||||
|
br := bufio.NewReader(rwc) |
||||||
|
bw := bufio.NewWriter(rwc) |
||||||
|
buf = bufio.NewReadWriter(br, bw) |
||||||
|
} |
||||||
|
ws := &Conn{config: config, request: request, buf: buf, rwc: rwc, |
||||||
|
frameReaderFactory: hybiFrameReaderFactory{buf.Reader}, |
||||||
|
frameWriterFactory: hybiFrameWriterFactory{ |
||||||
|
buf.Writer, request == nil}, |
||||||
|
PayloadType: TextFrame, |
||||||
|
defaultCloseStatus: closeStatusNormal} |
||||||
|
ws.frameHandler = &hybiFrameHandler{conn: ws} |
||||||
|
return ws |
||||||
|
} |
||||||
|
|
||||||
|
// generateMaskingKey generates a masking key for a frame.
|
||||||
|
func generateMaskingKey() (maskingKey []byte, err error) { |
||||||
|
maskingKey = make([]byte, 4) |
||||||
|
if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// generateNonce generates a nonce consisting of a randomly selected 16-byte
|
||||||
|
// value that has been base64-encoded.
|
||||||
|
func generateNonce() (nonce []byte) { |
||||||
|
key := make([]byte, 16) |
||||||
|
if _, err := io.ReadFull(rand.Reader, key); err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
nonce = make([]byte, 24) |
||||||
|
base64.StdEncoding.Encode(nonce, key) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of
|
||||||
|
// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string.
|
||||||
|
func getNonceAccept(nonce []byte) (expected []byte, err error) { |
||||||
|
h := sha1.New() |
||||||
|
if _, err = h.Write(nonce); err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
if _, err = h.Write([]byte(websocketGUID)); err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
expected = make([]byte, 28) |
||||||
|
base64.StdEncoding.Encode(expected, h.Sum(nil)) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17
|
||||||
|
func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { |
||||||
|
bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") |
||||||
|
|
||||||
|
bw.WriteString("Host: " + config.Location.Host + "\r\n") |
||||||
|
bw.WriteString("Upgrade: websocket\r\n") |
||||||
|
bw.WriteString("Connection: Upgrade\r\n") |
||||||
|
nonce := generateNonce() |
||||||
|
if config.handshakeData != nil { |
||||||
|
nonce = []byte(config.handshakeData["key"]) |
||||||
|
} |
||||||
|
bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n") |
||||||
|
bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n") |
||||||
|
|
||||||
|
if config.Version != ProtocolVersionHybi13 { |
||||||
|
return ErrBadProtocolVersion |
||||||
|
} |
||||||
|
|
||||||
|
bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n") |
||||||
|
if len(config.Protocol) > 0 { |
||||||
|
bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n") |
||||||
|
} |
||||||
|
// TODO(ukai): send Sec-WebSocket-Extensions.
|
||||||
|
err = config.Header.WriteSubset(bw, handshakeHeader) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
bw.WriteString("\r\n") |
||||||
|
if err = bw.Flush(); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if resp.StatusCode != 101 { |
||||||
|
return ErrBadStatus |
||||||
|
} |
||||||
|
if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" || |
||||||
|
strings.ToLower(resp.Header.Get("Connection")) != "upgrade" { |
||||||
|
return ErrBadUpgrade |
||||||
|
} |
||||||
|
expectedAccept, err := getNonceAccept(nonce) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) { |
||||||
|
return ErrChallengeResponse |
||||||
|
} |
||||||
|
if resp.Header.Get("Sec-WebSocket-Extensions") != "" { |
||||||
|
return ErrUnsupportedExtensions |
||||||
|
} |
||||||
|
offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol") |
||||||
|
if offeredProtocol != "" { |
||||||
|
protocolMatched := false |
||||||
|
for i := 0; i < len(config.Protocol); i++ { |
||||||
|
if config.Protocol[i] == offeredProtocol { |
||||||
|
protocolMatched = true |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
if !protocolMatched { |
||||||
|
return ErrBadWebSocketProtocol |
||||||
|
} |
||||||
|
config.Protocol = []string{offeredProtocol} |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// newHybiClientConn creates a client WebSocket connection after handshake.
|
||||||
|
func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn { |
||||||
|
return newHybiConn(config, buf, rwc, nil) |
||||||
|
} |
||||||
|
|
||||||
|
// A HybiServerHandshaker performs a server handshake using hybi draft protocol.
|
||||||
|
type hybiServerHandshaker struct { |
||||||
|
*Config |
||||||
|
accept []byte |
||||||
|
} |
||||||
|
|
||||||
|
func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { |
||||||
|
c.Version = ProtocolVersionHybi13 |
||||||
|
if req.Method != "GET" { |
||||||
|
return http.StatusMethodNotAllowed, ErrBadRequestMethod |
||||||
|
} |
||||||
|
// HTTP version can be safely ignored.
|
||||||
|
|
||||||
|
if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || |
||||||
|
!strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") { |
||||||
|
return http.StatusBadRequest, ErrNotWebSocket |
||||||
|
} |
||||||
|
|
||||||
|
key := req.Header.Get("Sec-Websocket-Key") |
||||||
|
if key == "" { |
||||||
|
return http.StatusBadRequest, ErrChallengeResponse |
||||||
|
} |
||||||
|
version := req.Header.Get("Sec-Websocket-Version") |
||||||
|
switch version { |
||||||
|
case "13": |
||||||
|
c.Version = ProtocolVersionHybi13 |
||||||
|
default: |
||||||
|
return http.StatusBadRequest, ErrBadWebSocketVersion |
||||||
|
} |
||||||
|
var scheme string |
||||||
|
if req.TLS != nil { |
||||||
|
scheme = "wss" |
||||||
|
} else { |
||||||
|
scheme = "ws" |
||||||
|
} |
||||||
|
c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) |
||||||
|
if err != nil { |
||||||
|
return http.StatusBadRequest, err |
||||||
|
} |
||||||
|
protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol")) |
||||||
|
if protocol != "" { |
||||||
|
protocols := strings.Split(protocol, ",") |
||||||
|
for i := 0; i < len(protocols); i++ { |
||||||
|
c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) |
||||||
|
} |
||||||
|
} |
||||||
|
c.accept, err = getNonceAccept([]byte(key)) |
||||||
|
if err != nil { |
||||||
|
return http.StatusInternalServerError, err |
||||||
|
} |
||||||
|
return http.StatusSwitchingProtocols, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Origin parses Origin header in "req".
|
||||||
|
// If origin is "null", returns (nil, nil).
|
||||||
|
func Origin(config *Config, req *http.Request) (*url.URL, error) { |
||||||
|
var origin string |
||||||
|
switch config.Version { |
||||||
|
case ProtocolVersionHybi13: |
||||||
|
origin = req.Header.Get("Origin") |
||||||
|
} |
||||||
|
if origin == "null" { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
return url.ParseRequestURI(origin) |
||||||
|
} |
||||||
|
|
||||||
|
func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { |
||||||
|
if len(c.Protocol) > 0 { |
||||||
|
if len(c.Protocol) != 1 { |
||||||
|
// You need choose a Protocol in Handshake func in Server.
|
||||||
|
return ErrBadWebSocketProtocol |
||||||
|
} |
||||||
|
} |
||||||
|
buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n") |
||||||
|
buf.WriteString("Upgrade: websocket\r\n") |
||||||
|
buf.WriteString("Connection: Upgrade\r\n") |
||||||
|
buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n") |
||||||
|
if len(c.Protocol) > 0 { |
||||||
|
buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n") |
||||||
|
} |
||||||
|
// TODO(ukai): send Sec-WebSocket-Extensions.
|
||||||
|
if c.Header != nil { |
||||||
|
err := c.Header.WriteSubset(buf, handshakeHeader) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
buf.WriteString("\r\n") |
||||||
|
return buf.Flush() |
||||||
|
} |
||||||
|
|
||||||
|
func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { |
||||||
|
return newHybiServerConn(c.Config, buf, rwc, request) |
||||||
|
} |
||||||
|
|
||||||
|
// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol.
|
||||||
|
func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { |
||||||
|
return newHybiConn(config, buf, rwc, request) |
||||||
|
} |
@ -0,0 +1,590 @@ |
|||||||
|
// Copyright 2011 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.
|
||||||
|
|
||||||
|
package websocket |
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"net/http" |
||||||
|
"net/url" |
||||||
|
"strings" |
||||||
|
"testing" |
||||||
|
) |
||||||
|
|
||||||
|
// Test the getNonceAccept function with values in
|
||||||
|
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
|
||||||
|
func TestSecWebSocketAccept(t *testing.T) { |
||||||
|
nonce := []byte("dGhlIHNhbXBsZSBub25jZQ==") |
||||||
|
expected := []byte("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=") |
||||||
|
accept, err := getNonceAccept(nonce) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("getNonceAccept: returned error %v", err) |
||||||
|
return |
||||||
|
} |
||||||
|
if !bytes.Equal(expected, accept) { |
||||||
|
t.Errorf("getNonceAccept: expected %q got %q", expected, accept) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiClientHandshake(t *testing.T) { |
||||||
|
b := bytes.NewBuffer([]byte{}) |
||||||
|
bw := bufio.NewWriter(b) |
||||||
|
br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols |
||||||
|
Upgrade: websocket |
||||||
|
Connection: Upgrade |
||||||
|
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= |
||||||
|
Sec-WebSocket-Protocol: chat |
||||||
|
|
||||||
|
`)) |
||||||
|
var err error |
||||||
|
config := new(Config) |
||||||
|
config.Location, err = url.ParseRequestURI("ws://server.example.com/chat") |
||||||
|
if err != nil { |
||||||
|
t.Fatal("location url", err) |
||||||
|
} |
||||||
|
config.Origin, err = url.ParseRequestURI("http://example.com") |
||||||
|
if err != nil { |
||||||
|
t.Fatal("origin url", err) |
||||||
|
} |
||||||
|
config.Protocol = append(config.Protocol, "chat") |
||||||
|
config.Protocol = append(config.Protocol, "superchat") |
||||||
|
config.Version = ProtocolVersionHybi13 |
||||||
|
|
||||||
|
config.handshakeData = map[string]string{ |
||||||
|
"key": "dGhlIHNhbXBsZSBub25jZQ==", |
||||||
|
} |
||||||
|
err = hybiClientHandshake(config, br, bw) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("handshake failed: %v", err) |
||||||
|
} |
||||||
|
req, err := http.ReadRequest(bufio.NewReader(b)) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("read request: %v", err) |
||||||
|
} |
||||||
|
if req.Method != "GET" { |
||||||
|
t.Errorf("request method expected GET, but got %q", req.Method) |
||||||
|
} |
||||||
|
if req.URL.Path != "/chat" { |
||||||
|
t.Errorf("request path expected /chat, but got %q", req.URL.Path) |
||||||
|
} |
||||||
|
if req.Proto != "HTTP/1.1" { |
||||||
|
t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) |
||||||
|
} |
||||||
|
if req.Host != "server.example.com" { |
||||||
|
t.Errorf("request Host expected server.example.com, but got %v", req.Host) |
||||||
|
} |
||||||
|
var expectedHeader = map[string]string{ |
||||||
|
"Connection": "Upgrade", |
||||||
|
"Upgrade": "websocket", |
||||||
|
"Sec-Websocket-Key": config.handshakeData["key"], |
||||||
|
"Origin": config.Origin.String(), |
||||||
|
"Sec-Websocket-Protocol": "chat, superchat", |
||||||
|
"Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13), |
||||||
|
} |
||||||
|
for k, v := range expectedHeader { |
||||||
|
if req.Header.Get(k) != v { |
||||||
|
t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiClientHandshakeWithHeader(t *testing.T) { |
||||||
|
b := bytes.NewBuffer([]byte{}) |
||||||
|
bw := bufio.NewWriter(b) |
||||||
|
br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols |
||||||
|
Upgrade: websocket |
||||||
|
Connection: Upgrade |
||||||
|
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= |
||||||
|
Sec-WebSocket-Protocol: chat |
||||||
|
|
||||||
|
`)) |
||||||
|
var err error |
||||||
|
config := new(Config) |
||||||
|
config.Location, err = url.ParseRequestURI("ws://server.example.com/chat") |
||||||
|
if err != nil { |
||||||
|
t.Fatal("location url", err) |
||||||
|
} |
||||||
|
config.Origin, err = url.ParseRequestURI("http://example.com") |
||||||
|
if err != nil { |
||||||
|
t.Fatal("origin url", err) |
||||||
|
} |
||||||
|
config.Protocol = append(config.Protocol, "chat") |
||||||
|
config.Protocol = append(config.Protocol, "superchat") |
||||||
|
config.Version = ProtocolVersionHybi13 |
||||||
|
config.Header = http.Header(make(map[string][]string)) |
||||||
|
config.Header.Add("User-Agent", "test") |
||||||
|
|
||||||
|
config.handshakeData = map[string]string{ |
||||||
|
"key": "dGhlIHNhbXBsZSBub25jZQ==", |
||||||
|
} |
||||||
|
err = hybiClientHandshake(config, br, bw) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("handshake failed: %v", err) |
||||||
|
} |
||||||
|
req, err := http.ReadRequest(bufio.NewReader(b)) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("read request: %v", err) |
||||||
|
} |
||||||
|
if req.Method != "GET" { |
||||||
|
t.Errorf("request method expected GET, but got %q", req.Method) |
||||||
|
} |
||||||
|
if req.URL.Path != "/chat" { |
||||||
|
t.Errorf("request path expected /chat, but got %q", req.URL.Path) |
||||||
|
} |
||||||
|
if req.Proto != "HTTP/1.1" { |
||||||
|
t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) |
||||||
|
} |
||||||
|
if req.Host != "server.example.com" { |
||||||
|
t.Errorf("request Host expected server.example.com, but got %v", req.Host) |
||||||
|
} |
||||||
|
var expectedHeader = map[string]string{ |
||||||
|
"Connection": "Upgrade", |
||||||
|
"Upgrade": "websocket", |
||||||
|
"Sec-Websocket-Key": config.handshakeData["key"], |
||||||
|
"Origin": config.Origin.String(), |
||||||
|
"Sec-Websocket-Protocol": "chat, superchat", |
||||||
|
"Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13), |
||||||
|
"User-Agent": "test", |
||||||
|
} |
||||||
|
for k, v := range expectedHeader { |
||||||
|
if req.Header.Get(k) != v { |
||||||
|
t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiServerHandshake(t *testing.T) { |
||||||
|
config := new(Config) |
||||||
|
handshaker := &hybiServerHandshaker{Config: config} |
||||||
|
br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 |
||||||
|
Host: server.example.com |
||||||
|
Upgrade: websocket |
||||||
|
Connection: Upgrade |
||||||
|
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== |
||||||
|
Origin: http://example.com
|
||||||
|
Sec-WebSocket-Protocol: chat, superchat |
||||||
|
Sec-WebSocket-Version: 13 |
||||||
|
|
||||||
|
`)) |
||||||
|
req, err := http.ReadRequest(br) |
||||||
|
if err != nil { |
||||||
|
t.Fatal("request", err) |
||||||
|
} |
||||||
|
code, err := handshaker.ReadHandshake(br, req) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("handshake failed: %v", err) |
||||||
|
} |
||||||
|
if code != http.StatusSwitchingProtocols { |
||||||
|
t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) |
||||||
|
} |
||||||
|
expectedProtocols := []string{"chat", "superchat"} |
||||||
|
if fmt.Sprintf("%v", config.Protocol) != fmt.Sprintf("%v", expectedProtocols) { |
||||||
|
t.Errorf("protocol expected %q but got %q", expectedProtocols, config.Protocol) |
||||||
|
} |
||||||
|
b := bytes.NewBuffer([]byte{}) |
||||||
|
bw := bufio.NewWriter(b) |
||||||
|
|
||||||
|
config.Protocol = config.Protocol[:1] |
||||||
|
|
||||||
|
err = handshaker.AcceptHandshake(bw) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("handshake response failed: %v", err) |
||||||
|
} |
||||||
|
expectedResponse := strings.Join([]string{ |
||||||
|
"HTTP/1.1 101 Switching Protocols", |
||||||
|
"Upgrade: websocket", |
||||||
|
"Connection: Upgrade", |
||||||
|
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", |
||||||
|
"Sec-WebSocket-Protocol: chat", |
||||||
|
"", ""}, "\r\n") |
||||||
|
|
||||||
|
if b.String() != expectedResponse { |
||||||
|
t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiServerHandshakeNoSubProtocol(t *testing.T) { |
||||||
|
config := new(Config) |
||||||
|
handshaker := &hybiServerHandshaker{Config: config} |
||||||
|
br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 |
||||||
|
Host: server.example.com |
||||||
|
Upgrade: websocket |
||||||
|
Connection: Upgrade |
||||||
|
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== |
||||||
|
Origin: http://example.com
|
||||||
|
Sec-WebSocket-Version: 13 |
||||||
|
|
||||||
|
`)) |
||||||
|
req, err := http.ReadRequest(br) |
||||||
|
if err != nil { |
||||||
|
t.Fatal("request", err) |
||||||
|
} |
||||||
|
code, err := handshaker.ReadHandshake(br, req) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("handshake failed: %v", err) |
||||||
|
} |
||||||
|
if code != http.StatusSwitchingProtocols { |
||||||
|
t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) |
||||||
|
} |
||||||
|
if len(config.Protocol) != 0 { |
||||||
|
t.Errorf("len(config.Protocol) expected 0, but got %q", len(config.Protocol)) |
||||||
|
} |
||||||
|
b := bytes.NewBuffer([]byte{}) |
||||||
|
bw := bufio.NewWriter(b) |
||||||
|
|
||||||
|
err = handshaker.AcceptHandshake(bw) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("handshake response failed: %v", err) |
||||||
|
} |
||||||
|
expectedResponse := strings.Join([]string{ |
||||||
|
"HTTP/1.1 101 Switching Protocols", |
||||||
|
"Upgrade: websocket", |
||||||
|
"Connection: Upgrade", |
||||||
|
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", |
||||||
|
"", ""}, "\r\n") |
||||||
|
|
||||||
|
if b.String() != expectedResponse { |
||||||
|
t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiServerHandshakeHybiBadVersion(t *testing.T) { |
||||||
|
config := new(Config) |
||||||
|
handshaker := &hybiServerHandshaker{Config: config} |
||||||
|
br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 |
||||||
|
Host: server.example.com |
||||||
|
Upgrade: websocket |
||||||
|
Connection: Upgrade |
||||||
|
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== |
||||||
|
Sec-WebSocket-Origin: http://example.com
|
||||||
|
Sec-WebSocket-Protocol: chat, superchat |
||||||
|
Sec-WebSocket-Version: 9 |
||||||
|
|
||||||
|
`)) |
||||||
|
req, err := http.ReadRequest(br) |
||||||
|
if err != nil { |
||||||
|
t.Fatal("request", err) |
||||||
|
} |
||||||
|
code, err := handshaker.ReadHandshake(br, req) |
||||||
|
if err != ErrBadWebSocketVersion { |
||||||
|
t.Errorf("handshake expected err %q but got %q", ErrBadWebSocketVersion, err) |
||||||
|
} |
||||||
|
if code != http.StatusBadRequest { |
||||||
|
t.Errorf("status expected %q but got %q", http.StatusBadRequest, code) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func testHybiFrame(t *testing.T, testHeader, testPayload, testMaskedPayload []byte, frameHeader *hybiFrameHeader) { |
||||||
|
b := bytes.NewBuffer([]byte{}) |
||||||
|
frameWriterFactory := &hybiFrameWriterFactory{bufio.NewWriter(b), false} |
||||||
|
w, _ := frameWriterFactory.NewFrameWriter(TextFrame) |
||||||
|
w.(*hybiFrameWriter).header = frameHeader |
||||||
|
_, err := w.Write(testPayload) |
||||||
|
w.Close() |
||||||
|
if err != nil { |
||||||
|
t.Errorf("Write error %q", err) |
||||||
|
} |
||||||
|
var expectedFrame []byte |
||||||
|
expectedFrame = append(expectedFrame, testHeader...) |
||||||
|
expectedFrame = append(expectedFrame, testMaskedPayload...) |
||||||
|
if !bytes.Equal(expectedFrame, b.Bytes()) { |
||||||
|
t.Errorf("frame expected %q got %q", expectedFrame, b.Bytes()) |
||||||
|
} |
||||||
|
frameReaderFactory := &hybiFrameReaderFactory{bufio.NewReader(b)} |
||||||
|
r, err := frameReaderFactory.NewFrameReader() |
||||||
|
if err != nil { |
||||||
|
t.Errorf("Read error %q", err) |
||||||
|
} |
||||||
|
if header := r.HeaderReader(); header == nil { |
||||||
|
t.Errorf("no header") |
||||||
|
} else { |
||||||
|
actualHeader := make([]byte, r.Len()) |
||||||
|
n, err := header.Read(actualHeader) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("Read header error %q", err) |
||||||
|
} else { |
||||||
|
if n < len(testHeader) { |
||||||
|
t.Errorf("header too short %q got %q", testHeader, actualHeader[:n]) |
||||||
|
} |
||||||
|
if !bytes.Equal(testHeader, actualHeader[:n]) { |
||||||
|
t.Errorf("header expected %q got %q", testHeader, actualHeader[:n]) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if trailer := r.TrailerReader(); trailer != nil { |
||||||
|
t.Errorf("unexpected trailer %q", trailer) |
||||||
|
} |
||||||
|
frame := r.(*hybiFrameReader) |
||||||
|
if frameHeader.Fin != frame.header.Fin || |
||||||
|
frameHeader.OpCode != frame.header.OpCode || |
||||||
|
len(testPayload) != int(frame.header.Length) { |
||||||
|
t.Errorf("mismatch %v (%d) vs %v", frameHeader, len(testPayload), frame) |
||||||
|
} |
||||||
|
payload := make([]byte, len(testPayload)) |
||||||
|
_, err = r.Read(payload) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("read %v", err) |
||||||
|
} |
||||||
|
if !bytes.Equal(testPayload, payload) { |
||||||
|
t.Errorf("payload %q vs %q", testPayload, payload) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiShortTextFrame(t *testing.T) { |
||||||
|
frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame} |
||||||
|
payload := []byte("hello") |
||||||
|
testHybiFrame(t, []byte{0x81, 0x05}, payload, payload, frameHeader) |
||||||
|
|
||||||
|
payload = make([]byte, 125) |
||||||
|
testHybiFrame(t, []byte{0x81, 125}, payload, payload, frameHeader) |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiShortMaskedTextFrame(t *testing.T) { |
||||||
|
frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame, |
||||||
|
MaskingKey: []byte{0xcc, 0x55, 0x80, 0x20}} |
||||||
|
payload := []byte("hello") |
||||||
|
maskedPayload := []byte{0xa4, 0x30, 0xec, 0x4c, 0xa3} |
||||||
|
header := []byte{0x81, 0x85} |
||||||
|
header = append(header, frameHeader.MaskingKey...) |
||||||
|
testHybiFrame(t, header, payload, maskedPayload, frameHeader) |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiShortBinaryFrame(t *testing.T) { |
||||||
|
frameHeader := &hybiFrameHeader{Fin: true, OpCode: BinaryFrame} |
||||||
|
payload := []byte("hello") |
||||||
|
testHybiFrame(t, []byte{0x82, 0x05}, payload, payload, frameHeader) |
||||||
|
|
||||||
|
payload = make([]byte, 125) |
||||||
|
testHybiFrame(t, []byte{0x82, 125}, payload, payload, frameHeader) |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiControlFrame(t *testing.T) { |
||||||
|
frameHeader := &hybiFrameHeader{Fin: true, OpCode: PingFrame} |
||||||
|
payload := []byte("hello") |
||||||
|
testHybiFrame(t, []byte{0x89, 0x05}, payload, payload, frameHeader) |
||||||
|
|
||||||
|
frameHeader = &hybiFrameHeader{Fin: true, OpCode: PongFrame} |
||||||
|
testHybiFrame(t, []byte{0x8A, 0x05}, payload, payload, frameHeader) |
||||||
|
|
||||||
|
frameHeader = &hybiFrameHeader{Fin: true, OpCode: CloseFrame} |
||||||
|
payload = []byte{0x03, 0xe8} // 1000
|
||||||
|
testHybiFrame(t, []byte{0x88, 0x02}, payload, payload, frameHeader) |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiLongFrame(t *testing.T) { |
||||||
|
frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame} |
||||||
|
payload := make([]byte, 126) |
||||||
|
testHybiFrame(t, []byte{0x81, 126, 0x00, 126}, payload, payload, frameHeader) |
||||||
|
|
||||||
|
payload = make([]byte, 65535) |
||||||
|
testHybiFrame(t, []byte{0x81, 126, 0xff, 0xff}, payload, payload, frameHeader) |
||||||
|
|
||||||
|
payload = make([]byte, 65536) |
||||||
|
testHybiFrame(t, []byte{0x81, 127, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}, payload, payload, frameHeader) |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiClientRead(t *testing.T) { |
||||||
|
wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o', |
||||||
|
0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping
|
||||||
|
0x81, 0x05, 'w', 'o', 'r', 'l', 'd'} |
||||||
|
br := bufio.NewReader(bytes.NewBuffer(wireData)) |
||||||
|
bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) |
||||||
|
conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) |
||||||
|
|
||||||
|
msg := make([]byte, 512) |
||||||
|
n, err := conn.Read(msg) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("read 1st frame, error %q", err) |
||||||
|
} |
||||||
|
if n != 5 { |
||||||
|
t.Errorf("read 1st frame, expect 5, got %d", n) |
||||||
|
} |
||||||
|
if !bytes.Equal(wireData[2:7], msg[:n]) { |
||||||
|
t.Errorf("read 1st frame %v, got %v", wireData[2:7], msg[:n]) |
||||||
|
} |
||||||
|
n, err = conn.Read(msg) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("read 2nd frame, error %q", err) |
||||||
|
} |
||||||
|
if n != 5 { |
||||||
|
t.Errorf("read 2nd frame, expect 5, got %d", n) |
||||||
|
} |
||||||
|
if !bytes.Equal(wireData[16:21], msg[:n]) { |
||||||
|
t.Errorf("read 2nd frame %v, got %v", wireData[16:21], msg[:n]) |
||||||
|
} |
||||||
|
n, err = conn.Read(msg) |
||||||
|
if err == nil { |
||||||
|
t.Errorf("read not EOF") |
||||||
|
} |
||||||
|
if n != 0 { |
||||||
|
t.Errorf("expect read 0, got %d", n) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiShortRead(t *testing.T) { |
||||||
|
wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o', |
||||||
|
0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping
|
||||||
|
0x81, 0x05, 'w', 'o', 'r', 'l', 'd'} |
||||||
|
br := bufio.NewReader(bytes.NewBuffer(wireData)) |
||||||
|
bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) |
||||||
|
conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) |
||||||
|
|
||||||
|
step := 0 |
||||||
|
pos := 0 |
||||||
|
expectedPos := []int{2, 5, 16, 19} |
||||||
|
expectedLen := []int{3, 2, 3, 2} |
||||||
|
for { |
||||||
|
msg := make([]byte, 3) |
||||||
|
n, err := conn.Read(msg) |
||||||
|
if step >= len(expectedPos) { |
||||||
|
if err == nil { |
||||||
|
t.Errorf("read not EOF") |
||||||
|
} |
||||||
|
if n != 0 { |
||||||
|
t.Errorf("expect read 0, got %d", n) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
pos = expectedPos[step] |
||||||
|
endPos := pos + expectedLen[step] |
||||||
|
if err != nil { |
||||||
|
t.Errorf("read from %d, got error %q", pos, err) |
||||||
|
return |
||||||
|
} |
||||||
|
if n != endPos-pos { |
||||||
|
t.Errorf("read from %d, expect %d, got %d", pos, endPos-pos, n) |
||||||
|
} |
||||||
|
if !bytes.Equal(wireData[pos:endPos], msg[:n]) { |
||||||
|
t.Errorf("read from %d, frame %v, got %v", pos, wireData[pos:endPos], msg[:n]) |
||||||
|
} |
||||||
|
step++ |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiServerRead(t *testing.T) { |
||||||
|
wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20, |
||||||
|
0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello
|
||||||
|
0x89, 0x85, 0xcc, 0x55, 0x80, 0x20, |
||||||
|
0xa4, 0x30, 0xec, 0x4c, 0xa3, // ping: hello
|
||||||
|
0x81, 0x85, 0xed, 0x83, 0xb4, 0x24, |
||||||
|
0x9a, 0xec, 0xc6, 0x48, 0x89, // world
|
||||||
|
} |
||||||
|
br := bufio.NewReader(bytes.NewBuffer(wireData)) |
||||||
|
bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) |
||||||
|
conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request)) |
||||||
|
|
||||||
|
expected := [][]byte{[]byte("hello"), []byte("world")} |
||||||
|
|
||||||
|
msg := make([]byte, 512) |
||||||
|
n, err := conn.Read(msg) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("read 1st frame, error %q", err) |
||||||
|
} |
||||||
|
if n != 5 { |
||||||
|
t.Errorf("read 1st frame, expect 5, got %d", n) |
||||||
|
} |
||||||
|
if !bytes.Equal(expected[0], msg[:n]) { |
||||||
|
t.Errorf("read 1st frame %q, got %q", expected[0], msg[:n]) |
||||||
|
} |
||||||
|
|
||||||
|
n, err = conn.Read(msg) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("read 2nd frame, error %q", err) |
||||||
|
} |
||||||
|
if n != 5 { |
||||||
|
t.Errorf("read 2nd frame, expect 5, got %d", n) |
||||||
|
} |
||||||
|
if !bytes.Equal(expected[1], msg[:n]) { |
||||||
|
t.Errorf("read 2nd frame %q, got %q", expected[1], msg[:n]) |
||||||
|
} |
||||||
|
|
||||||
|
n, err = conn.Read(msg) |
||||||
|
if err == nil { |
||||||
|
t.Errorf("read not EOF") |
||||||
|
} |
||||||
|
if n != 0 { |
||||||
|
t.Errorf("expect read 0, got %d", n) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiServerReadWithoutMasking(t *testing.T) { |
||||||
|
wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o'} |
||||||
|
br := bufio.NewReader(bytes.NewBuffer(wireData)) |
||||||
|
bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) |
||||||
|
conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request)) |
||||||
|
// server MUST close the connection upon receiving a non-masked frame.
|
||||||
|
msg := make([]byte, 512) |
||||||
|
_, err := conn.Read(msg) |
||||||
|
if err != io.EOF { |
||||||
|
t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestHybiClientReadWithMasking(t *testing.T) { |
||||||
|
wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20, |
||||||
|
0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello
|
||||||
|
} |
||||||
|
br := bufio.NewReader(bytes.NewBuffer(wireData)) |
||||||
|
bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) |
||||||
|
conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) |
||||||
|
|
||||||
|
// client MUST close the connection upon receiving a masked frame.
|
||||||
|
msg := make([]byte, 512) |
||||||
|
_, err := conn.Read(msg) |
||||||
|
if err != io.EOF { |
||||||
|
t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Test the hybiServerHandshaker supports firefox implementation and
|
||||||
|
// checks Connection request header include (but it's not necessary
|
||||||
|
// equal to) "upgrade"
|
||||||
|
func TestHybiServerFirefoxHandshake(t *testing.T) { |
||||||
|
config := new(Config) |
||||||
|
handshaker := &hybiServerHandshaker{Config: config} |
||||||
|
br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 |
||||||
|
Host: server.example.com |
||||||
|
Upgrade: websocket |
||||||
|
Connection: keep-alive, upgrade |
||||||
|
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== |
||||||
|
Origin: http://example.com
|
||||||
|
Sec-WebSocket-Protocol: chat, superchat |
||||||
|
Sec-WebSocket-Version: 13 |
||||||
|
|
||||||
|
`)) |
||||||
|
req, err := http.ReadRequest(br) |
||||||
|
if err != nil { |
||||||
|
t.Fatal("request", err) |
||||||
|
} |
||||||
|
code, err := handshaker.ReadHandshake(br, req) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("handshake failed: %v", err) |
||||||
|
} |
||||||
|
if code != http.StatusSwitchingProtocols { |
||||||
|
t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) |
||||||
|
} |
||||||
|
b := bytes.NewBuffer([]byte{}) |
||||||
|
bw := bufio.NewWriter(b) |
||||||
|
|
||||||
|
config.Protocol = []string{"chat"} |
||||||
|
|
||||||
|
err = handshaker.AcceptHandshake(bw) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("handshake response failed: %v", err) |
||||||
|
} |
||||||
|
expectedResponse := strings.Join([]string{ |
||||||
|
"HTTP/1.1 101 Switching Protocols", |
||||||
|
"Upgrade: websocket", |
||||||
|
"Connection: Upgrade", |
||||||
|
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", |
||||||
|
"Sec-WebSocket-Protocol: chat", |
||||||
|
"", ""}, "\r\n") |
||||||
|
|
||||||
|
if b.String() != expectedResponse { |
||||||
|
t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,114 @@ |
|||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
package websocket |
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"net/http" |
||||||
|
) |
||||||
|
|
||||||
|
func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) { |
||||||
|
var hs serverHandshaker = &hybiServerHandshaker{Config: config} |
||||||
|
code, err := hs.ReadHandshake(buf.Reader, req) |
||||||
|
if err == ErrBadWebSocketVersion { |
||||||
|
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) |
||||||
|
fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion) |
||||||
|
buf.WriteString("\r\n") |
||||||
|
buf.WriteString(err.Error()) |
||||||
|
buf.Flush() |
||||||
|
return |
||||||
|
} |
||||||
|
if err != nil { |
||||||
|
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) |
||||||
|
buf.WriteString("\r\n") |
||||||
|
buf.WriteString(err.Error()) |
||||||
|
buf.Flush() |
||||||
|
return |
||||||
|
} |
||||||
|
if handshake != nil { |
||||||
|
err = handshake(config, req) |
||||||
|
if err != nil { |
||||||
|
code = http.StatusForbidden |
||||||
|
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) |
||||||
|
buf.WriteString("\r\n") |
||||||
|
buf.Flush() |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
err = hs.AcceptHandshake(buf.Writer) |
||||||
|
if err != nil { |
||||||
|
code = http.StatusBadRequest |
||||||
|
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) |
||||||
|
buf.WriteString("\r\n") |
||||||
|
buf.Flush() |
||||||
|
return |
||||||
|
} |
||||||
|
conn = hs.NewServerConn(buf, rwc, req) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// Server represents a server of a WebSocket.
|
||||||
|
type Server struct { |
||||||
|
// Config is a WebSocket configuration for new WebSocket connection.
|
||||||
|
Config |
||||||
|
|
||||||
|
// Handshake is an optional function in WebSocket handshake.
|
||||||
|
// For example, you can check, or don't check Origin header.
|
||||||
|
// Another example, you can select config.Protocol.
|
||||||
|
Handshake func(*Config, *http.Request) error |
||||||
|
|
||||||
|
// Handler handles a WebSocket connection.
|
||||||
|
Handler |
||||||
|
} |
||||||
|
|
||||||
|
// ServeHTTP implements the http.Handler interface for a WebSocket
|
||||||
|
func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { |
||||||
|
s.serveWebSocket(w, req) |
||||||
|
} |
||||||
|
|
||||||
|
func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) { |
||||||
|
rwc, buf, err := w.(http.Hijacker).Hijack() |
||||||
|
if err != nil { |
||||||
|
panic("Hijack failed: " + err.Error()) |
||||||
|
return |
||||||
|
} |
||||||
|
// The server should abort the WebSocket connection if it finds
|
||||||
|
// the client did not send a handshake that matches with protocol
|
||||||
|
// specification.
|
||||||
|
defer rwc.Close() |
||||||
|
conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
if conn == nil { |
||||||
|
panic("unexpected nil conn") |
||||||
|
} |
||||||
|
s.Handler(conn) |
||||||
|
} |
||||||
|
|
||||||
|
// Handler is a simple interface to a WebSocket browser client.
|
||||||
|
// It checks if Origin header is valid URL by default.
|
||||||
|
// You might want to verify websocket.Conn.Config().Origin in the func.
|
||||||
|
// If you use Server instead of Handler, you could call websocket.Origin and
|
||||||
|
// check the origin in your Handshake func. So, if you want to accept
|
||||||
|
// non-browser client, which doesn't send Origin header, you could use Server
|
||||||
|
//. that doesn't check origin in its Handshake.
|
||||||
|
type Handler func(*Conn) |
||||||
|
|
||||||
|
func checkOrigin(config *Config, req *http.Request) (err error) { |
||||||
|
config.Origin, err = Origin(config, req) |
||||||
|
if err == nil && config.Origin == nil { |
||||||
|
return fmt.Errorf("null origin") |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// ServeHTTP implements the http.Handler interface for a WebSocket
|
||||||
|
func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { |
||||||
|
s := Server{Handler: h, Handshake: checkOrigin} |
||||||
|
s.serveWebSocket(w, req) |
||||||
|
} |
@ -0,0 +1,411 @@ |
|||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
// Package websocket implements a client and server for the WebSocket protocol
|
||||||
|
// as specified in RFC 6455.
|
||||||
|
package websocket |
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"crypto/tls" |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
"io" |
||||||
|
"io/ioutil" |
||||||
|
"net" |
||||||
|
"net/http" |
||||||
|
"net/url" |
||||||
|
"sync" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
ProtocolVersionHybi13 = 13 |
||||||
|
ProtocolVersionHybi = ProtocolVersionHybi13 |
||||||
|
SupportedProtocolVersion = "13" |
||||||
|
|
||||||
|
ContinuationFrame = 0 |
||||||
|
TextFrame = 1 |
||||||
|
BinaryFrame = 2 |
||||||
|
CloseFrame = 8 |
||||||
|
PingFrame = 9 |
||||||
|
PongFrame = 10 |
||||||
|
UnknownFrame = 255 |
||||||
|
) |
||||||
|
|
||||||
|
// ProtocolError represents WebSocket protocol errors.
|
||||||
|
type ProtocolError struct { |
||||||
|
ErrorString string |
||||||
|
} |
||||||
|
|
||||||
|
func (err *ProtocolError) Error() string { return err.ErrorString } |
||||||
|
|
||||||
|
var ( |
||||||
|
ErrBadProtocolVersion = &ProtocolError{"bad protocol version"} |
||||||
|
ErrBadScheme = &ProtocolError{"bad scheme"} |
||||||
|
ErrBadStatus = &ProtocolError{"bad status"} |
||||||
|
ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"} |
||||||
|
ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"} |
||||||
|
ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"} |
||||||
|
ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"} |
||||||
|
ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"} |
||||||
|
ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"} |
||||||
|
ErrBadFrame = &ProtocolError{"bad frame"} |
||||||
|
ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"} |
||||||
|
ErrNotWebSocket = &ProtocolError{"not websocket protocol"} |
||||||
|
ErrBadRequestMethod = &ProtocolError{"bad method"} |
||||||
|
ErrNotSupported = &ProtocolError{"not supported"} |
||||||
|
) |
||||||
|
|
||||||
|
// Addr is an implementation of net.Addr for WebSocket.
|
||||||
|
type Addr struct { |
||||||
|
*url.URL |
||||||
|
} |
||||||
|
|
||||||
|
// Network returns the network type for a WebSocket, "websocket".
|
||||||
|
func (addr *Addr) Network() string { return "websocket" } |
||||||
|
|
||||||
|
// Config is a WebSocket configuration
|
||||||
|
type Config struct { |
||||||
|
// A WebSocket server address.
|
||||||
|
Location *url.URL |
||||||
|
|
||||||
|
// A Websocket client origin.
|
||||||
|
Origin *url.URL |
||||||
|
|
||||||
|
// WebSocket subprotocols.
|
||||||
|
Protocol []string |
||||||
|
|
||||||
|
// WebSocket protocol version.
|
||||||
|
Version int |
||||||
|
|
||||||
|
// TLS config for secure WebSocket (wss).
|
||||||
|
TlsConfig *tls.Config |
||||||
|
|
||||||
|
// Additional header fields to be sent in WebSocket opening handshake.
|
||||||
|
Header http.Header |
||||||
|
|
||||||
|
handshakeData map[string]string |
||||||
|
} |
||||||
|
|
||||||
|
// serverHandshaker is an interface to handle WebSocket server side handshake.
|
||||||
|
type serverHandshaker interface { |
||||||
|
// ReadHandshake reads handshake request message from client.
|
||||||
|
// Returns http response code and error if any.
|
||||||
|
ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) |
||||||
|
|
||||||
|
// AcceptHandshake accepts the client handshake request and sends
|
||||||
|
// handshake response back to client.
|
||||||
|
AcceptHandshake(buf *bufio.Writer) (err error) |
||||||
|
|
||||||
|
// NewServerConn creates a new WebSocket connection.
|
||||||
|
NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) |
||||||
|
} |
||||||
|
|
||||||
|
// frameReader is an interface to read a WebSocket frame.
|
||||||
|
type frameReader interface { |
||||||
|
// Reader is to read payload of the frame.
|
||||||
|
io.Reader |
||||||
|
|
||||||
|
// PayloadType returns payload type.
|
||||||
|
PayloadType() byte |
||||||
|
|
||||||
|
// HeaderReader returns a reader to read header of the frame.
|
||||||
|
HeaderReader() io.Reader |
||||||
|
|
||||||
|
// TrailerReader returns a reader to read trailer of the frame.
|
||||||
|
// If it returns nil, there is no trailer in the frame.
|
||||||
|
TrailerReader() io.Reader |
||||||
|
|
||||||
|
// Len returns total length of the frame, including header and trailer.
|
||||||
|
Len() int |
||||||
|
} |
||||||
|
|
||||||
|
// frameReaderFactory is an interface to creates new frame reader.
|
||||||
|
type frameReaderFactory interface { |
||||||
|
NewFrameReader() (r frameReader, err error) |
||||||
|
} |
||||||
|
|
||||||
|
// frameWriter is an interface to write a WebSocket frame.
|
||||||
|
type frameWriter interface { |
||||||
|
// Writer is to write payload of the frame.
|
||||||
|
io.WriteCloser |
||||||
|
} |
||||||
|
|
||||||
|
// frameWriterFactory is an interface to create new frame writer.
|
||||||
|
type frameWriterFactory interface { |
||||||
|
NewFrameWriter(payloadType byte) (w frameWriter, err error) |
||||||
|
} |
||||||
|
|
||||||
|
type frameHandler interface { |
||||||
|
HandleFrame(frame frameReader) (r frameReader, err error) |
||||||
|
WriteClose(status int) (err error) |
||||||
|
} |
||||||
|
|
||||||
|
// Conn represents a WebSocket connection.
|
||||||
|
type Conn struct { |
||||||
|
config *Config |
||||||
|
request *http.Request |
||||||
|
|
||||||
|
buf *bufio.ReadWriter |
||||||
|
rwc io.ReadWriteCloser |
||||||
|
|
||||||
|
rio sync.Mutex |
||||||
|
frameReaderFactory |
||||||
|
frameReader |
||||||
|
|
||||||
|
wio sync.Mutex |
||||||
|
frameWriterFactory |
||||||
|
|
||||||
|
frameHandler |
||||||
|
PayloadType byte |
||||||
|
defaultCloseStatus int |
||||||
|
} |
||||||
|
|
||||||
|
// Read implements the io.Reader interface:
|
||||||
|
// it reads data of a frame from the WebSocket connection.
|
||||||
|
// if msg is not large enough for the frame data, it fills the msg and next Read
|
||||||
|
// will read the rest of the frame data.
|
||||||
|
// it reads Text frame or Binary frame.
|
||||||
|
func (ws *Conn) Read(msg []byte) (n int, err error) { |
||||||
|
ws.rio.Lock() |
||||||
|
defer ws.rio.Unlock() |
||||||
|
again: |
||||||
|
if ws.frameReader == nil { |
||||||
|
frame, err := ws.frameReaderFactory.NewFrameReader() |
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
ws.frameReader, err = ws.frameHandler.HandleFrame(frame) |
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
if ws.frameReader == nil { |
||||||
|
goto again |
||||||
|
} |
||||||
|
} |
||||||
|
n, err = ws.frameReader.Read(msg) |
||||||
|
if err == io.EOF { |
||||||
|
if trailer := ws.frameReader.TrailerReader(); trailer != nil { |
||||||
|
io.Copy(ioutil.Discard, trailer) |
||||||
|
} |
||||||
|
ws.frameReader = nil |
||||||
|
goto again |
||||||
|
} |
||||||
|
return n, err |
||||||
|
} |
||||||
|
|
||||||
|
// Write implements the io.Writer interface:
|
||||||
|
// it writes data as a frame to the WebSocket connection.
|
||||||
|
func (ws *Conn) Write(msg []byte) (n int, err error) { |
||||||
|
ws.wio.Lock() |
||||||
|
defer ws.wio.Unlock() |
||||||
|
w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType) |
||||||
|
if err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
n, err = w.Write(msg) |
||||||
|
w.Close() |
||||||
|
if err != nil { |
||||||
|
return n, err |
||||||
|
} |
||||||
|
return n, err |
||||||
|
} |
||||||
|
|
||||||
|
// Close implements the io.Closer interface.
|
||||||
|
func (ws *Conn) Close() error { |
||||||
|
err := ws.frameHandler.WriteClose(ws.defaultCloseStatus) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return ws.rwc.Close() |
||||||
|
} |
||||||
|
|
||||||
|
func (ws *Conn) IsClientConn() bool { return ws.request == nil } |
||||||
|
func (ws *Conn) IsServerConn() bool { return ws.request != nil } |
||||||
|
|
||||||
|
// LocalAddr returns the WebSocket Origin for the connection for client, or
|
||||||
|
// the WebSocket location for server.
|
||||||
|
func (ws *Conn) LocalAddr() net.Addr { |
||||||
|
if ws.IsClientConn() { |
||||||
|
return &Addr{ws.config.Origin} |
||||||
|
} |
||||||
|
return &Addr{ws.config.Location} |
||||||
|
} |
||||||
|
|
||||||
|
// RemoteAddr returns the WebSocket location for the connection for client, or
|
||||||
|
// the Websocket Origin for server.
|
||||||
|
func (ws *Conn) RemoteAddr() net.Addr { |
||||||
|
if ws.IsClientConn() { |
||||||
|
return &Addr{ws.config.Location} |
||||||
|
} |
||||||
|
return &Addr{ws.config.Origin} |
||||||
|
} |
||||||
|
|
||||||
|
var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn") |
||||||
|
|
||||||
|
// SetDeadline sets the connection's network read & write deadlines.
|
||||||
|
func (ws *Conn) SetDeadline(t time.Time) error { |
||||||
|
if conn, ok := ws.rwc.(net.Conn); ok { |
||||||
|
return conn.SetDeadline(t) |
||||||
|
} |
||||||
|
return errSetDeadline |
||||||
|
} |
||||||
|
|
||||||
|
// SetReadDeadline sets the connection's network read deadline.
|
||||||
|
func (ws *Conn) SetReadDeadline(t time.Time) error { |
||||||
|
if conn, ok := ws.rwc.(net.Conn); ok { |
||||||
|
return conn.SetReadDeadline(t) |
||||||
|
} |
||||||
|
return errSetDeadline |
||||||
|
} |
||||||
|
|
||||||
|
// SetWriteDeadline sets the connection's network write deadline.
|
||||||
|
func (ws *Conn) SetWriteDeadline(t time.Time) error { |
||||||
|
if conn, ok := ws.rwc.(net.Conn); ok { |
||||||
|
return conn.SetWriteDeadline(t) |
||||||
|
} |
||||||
|
return errSetDeadline |
||||||
|
} |
||||||
|
|
||||||
|
// Config returns the WebSocket config.
|
||||||
|
func (ws *Conn) Config() *Config { return ws.config } |
||||||
|
|
||||||
|
// Request returns the http request upgraded to the WebSocket.
|
||||||
|
// It is nil for client side.
|
||||||
|
func (ws *Conn) Request() *http.Request { return ws.request } |
||||||
|
|
||||||
|
// Codec represents a symmetric pair of functions that implement a codec.
|
||||||
|
type Codec struct { |
||||||
|
Marshal func(v interface{}) (data []byte, payloadType byte, err error) |
||||||
|
Unmarshal func(data []byte, payloadType byte, v interface{}) (err error) |
||||||
|
} |
||||||
|
|
||||||
|
// Send sends v marshaled by cd.Marshal as single frame to ws.
|
||||||
|
func (cd Codec) Send(ws *Conn, v interface{}) (err error) { |
||||||
|
data, payloadType, err := cd.Marshal(v) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
ws.wio.Lock() |
||||||
|
defer ws.wio.Unlock() |
||||||
|
w, err := ws.frameWriterFactory.NewFrameWriter(payloadType) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
_, err = w.Write(data) |
||||||
|
w.Close() |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v.
|
||||||
|
func (cd Codec) Receive(ws *Conn, v interface{}) (err error) { |
||||||
|
ws.rio.Lock() |
||||||
|
defer ws.rio.Unlock() |
||||||
|
if ws.frameReader != nil { |
||||||
|
_, err = io.Copy(ioutil.Discard, ws.frameReader) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
ws.frameReader = nil |
||||||
|
} |
||||||
|
again: |
||||||
|
frame, err := ws.frameReaderFactory.NewFrameReader() |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
frame, err = ws.frameHandler.HandleFrame(frame) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if frame == nil { |
||||||
|
goto again |
||||||
|
} |
||||||
|
payloadType := frame.PayloadType() |
||||||
|
data, err := ioutil.ReadAll(frame) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return cd.Unmarshal(data, payloadType, v) |
||||||
|
} |
||||||
|
|
||||||
|
func marshal(v interface{}) (msg []byte, payloadType byte, err error) { |
||||||
|
switch data := v.(type) { |
||||||
|
case string: |
||||||
|
return []byte(data), TextFrame, nil |
||||||
|
case []byte: |
||||||
|
return data, BinaryFrame, nil |
||||||
|
} |
||||||
|
return nil, UnknownFrame, ErrNotSupported |
||||||
|
} |
||||||
|
|
||||||
|
func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) { |
||||||
|
switch data := v.(type) { |
||||||
|
case *string: |
||||||
|
*data = string(msg) |
||||||
|
return nil |
||||||
|
case *[]byte: |
||||||
|
*data = msg |
||||||
|
return nil |
||||||
|
} |
||||||
|
return ErrNotSupported |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
Message is a codec to send/receive text/binary data in a frame on WebSocket connection. |
||||||
|
To send/receive text frame, use string type. |
||||||
|
To send/receive binary frame, use []byte type. |
||||||
|
|
||||||
|
Trivial usage: |
||||||
|
|
||||||
|
import "websocket" |
||||||
|
|
||||||
|
// receive text frame
|
||||||
|
var message string |
||||||
|
websocket.Message.Receive(ws, &message) |
||||||
|
|
||||||
|
// send text frame
|
||||||
|
message = "hello" |
||||||
|
websocket.Message.Send(ws, message) |
||||||
|
|
||||||
|
// receive binary frame
|
||||||
|
var data []byte |
||||||
|
websocket.Message.Receive(ws, &data) |
||||||
|
|
||||||
|
// send binary frame
|
||||||
|
data = []byte{0, 1, 2} |
||||||
|
websocket.Message.Send(ws, data) |
||||||
|
|
||||||
|
*/ |
||||||
|
var Message = Codec{marshal, unmarshal} |
||||||
|
|
||||||
|
func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) { |
||||||
|
msg, err = json.Marshal(v) |
||||||
|
return msg, TextFrame, err |
||||||
|
} |
||||||
|
|
||||||
|
func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) { |
||||||
|
return json.Unmarshal(msg, v) |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
JSON is a codec to send/receive JSON data in a frame from a WebSocket connection. |
||||||
|
|
||||||
|
Trivial usage: |
||||||
|
|
||||||
|
import "websocket" |
||||||
|
|
||||||
|
type T struct { |
||||||
|
Msg string |
||||||
|
Count int |
||||||
|
} |
||||||
|
|
||||||
|
// receive JSON type T
|
||||||
|
var data T |
||||||
|
websocket.JSON.Receive(ws, &data) |
||||||
|
|
||||||
|
// send JSON type T
|
||||||
|
websocket.JSON.Send(ws, data) |
||||||
|
*/ |
||||||
|
var JSON = Codec{jsonMarshal, jsonUnmarshal} |
@ -0,0 +1,414 @@ |
|||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
package websocket |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"log" |
||||||
|
"net" |
||||||
|
"net/http" |
||||||
|
"net/http/httptest" |
||||||
|
"net/url" |
||||||
|
"strings" |
||||||
|
"sync" |
||||||
|
"testing" |
||||||
|
) |
||||||
|
|
||||||
|
var serverAddr string |
||||||
|
var once sync.Once |
||||||
|
|
||||||
|
func echoServer(ws *Conn) { io.Copy(ws, ws) } |
||||||
|
|
||||||
|
type Count struct { |
||||||
|
S string |
||||||
|
N int |
||||||
|
} |
||||||
|
|
||||||
|
func countServer(ws *Conn) { |
||||||
|
for { |
||||||
|
var count Count |
||||||
|
err := JSON.Receive(ws, &count) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
count.N++ |
||||||
|
count.S = strings.Repeat(count.S, count.N) |
||||||
|
err = JSON.Send(ws, count) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func subProtocolHandshake(config *Config, req *http.Request) error { |
||||||
|
for _, proto := range config.Protocol { |
||||||
|
if proto == "chat" { |
||||||
|
config.Protocol = []string{proto} |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
return ErrBadWebSocketProtocol |
||||||
|
} |
||||||
|
|
||||||
|
func subProtoServer(ws *Conn) { |
||||||
|
for _, proto := range ws.Config().Protocol { |
||||||
|
io.WriteString(ws, proto) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func startServer() { |
||||||
|
http.Handle("/echo", Handler(echoServer)) |
||||||
|
http.Handle("/count", Handler(countServer)) |
||||||
|
subproto := Server{ |
||||||
|
Handshake: subProtocolHandshake, |
||||||
|
Handler: Handler(subProtoServer), |
||||||
|
} |
||||||
|
http.Handle("/subproto", subproto) |
||||||
|
server := httptest.NewServer(nil) |
||||||
|
serverAddr = server.Listener.Addr().String() |
||||||
|
log.Print("Test WebSocket server listening on ", serverAddr) |
||||||
|
} |
||||||
|
|
||||||
|
func newConfig(t *testing.T, path string) *Config { |
||||||
|
config, _ := NewConfig(fmt.Sprintf("ws://%s%s", serverAddr, path), "http://localhost") |
||||||
|
return config |
||||||
|
} |
||||||
|
|
||||||
|
func TestEcho(t *testing.T) { |
||||||
|
once.Do(startServer) |
||||||
|
|
||||||
|
// websocket.Dial()
|
||||||
|
client, err := net.Dial("tcp", serverAddr) |
||||||
|
if err != nil { |
||||||
|
t.Fatal("dialing", err) |
||||||
|
} |
||||||
|
conn, err := NewClient(newConfig(t, "/echo"), client) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("WebSocket handshake error: %v", err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
msg := []byte("hello, world\n") |
||||||
|
if _, err := conn.Write(msg); err != nil { |
||||||
|
t.Errorf("Write: %v", err) |
||||||
|
} |
||||||
|
var actual_msg = make([]byte, 512) |
||||||
|
n, err := conn.Read(actual_msg) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("Read: %v", err) |
||||||
|
} |
||||||
|
actual_msg = actual_msg[0:n] |
||||||
|
if !bytes.Equal(msg, actual_msg) { |
||||||
|
t.Errorf("Echo: expected %q got %q", msg, actual_msg) |
||||||
|
} |
||||||
|
conn.Close() |
||||||
|
} |
||||||
|
|
||||||
|
func TestAddr(t *testing.T) { |
||||||
|
once.Do(startServer) |
||||||
|
|
||||||
|
// websocket.Dial()
|
||||||
|
client, err := net.Dial("tcp", serverAddr) |
||||||
|
if err != nil { |
||||||
|
t.Fatal("dialing", err) |
||||||
|
} |
||||||
|
conn, err := NewClient(newConfig(t, "/echo"), client) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("WebSocket handshake error: %v", err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
ra := conn.RemoteAddr().String() |
||||||
|
if !strings.HasPrefix(ra, "ws://") || !strings.HasSuffix(ra, "/echo") { |
||||||
|
t.Errorf("Bad remote addr: %v", ra) |
||||||
|
} |
||||||
|
la := conn.LocalAddr().String() |
||||||
|
if !strings.HasPrefix(la, "http://") { |
||||||
|
t.Errorf("Bad local addr: %v", la) |
||||||
|
} |
||||||
|
conn.Close() |
||||||
|
} |
||||||
|
|
||||||
|
func TestCount(t *testing.T) { |
||||||
|
once.Do(startServer) |
||||||
|
|
||||||
|
// websocket.Dial()
|
||||||
|
client, err := net.Dial("tcp", serverAddr) |
||||||
|
if err != nil { |
||||||
|
t.Fatal("dialing", err) |
||||||
|
} |
||||||
|
conn, err := NewClient(newConfig(t, "/count"), client) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("WebSocket handshake error: %v", err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
var count Count |
||||||
|
count.S = "hello" |
||||||
|
if err := JSON.Send(conn, count); err != nil { |
||||||
|
t.Errorf("Write: %v", err) |
||||||
|
} |
||||||
|
if err := JSON.Receive(conn, &count); err != nil { |
||||||
|
t.Errorf("Read: %v", err) |
||||||
|
} |
||||||
|
if count.N != 1 { |
||||||
|
t.Errorf("count: expected %d got %d", 1, count.N) |
||||||
|
} |
||||||
|
if count.S != "hello" { |
||||||
|
t.Errorf("count: expected %q got %q", "hello", count.S) |
||||||
|
} |
||||||
|
if err := JSON.Send(conn, count); err != nil { |
||||||
|
t.Errorf("Write: %v", err) |
||||||
|
} |
||||||
|
if err := JSON.Receive(conn, &count); err != nil { |
||||||
|
t.Errorf("Read: %v", err) |
||||||
|
} |
||||||
|
if count.N != 2 { |
||||||
|
t.Errorf("count: expected %d got %d", 2, count.N) |
||||||
|
} |
||||||
|
if count.S != "hellohello" { |
||||||
|
t.Errorf("count: expected %q got %q", "hellohello", count.S) |
||||||
|
} |
||||||
|
conn.Close() |
||||||
|
} |
||||||
|
|
||||||
|
func TestWithQuery(t *testing.T) { |
||||||
|
once.Do(startServer) |
||||||
|
|
||||||
|
client, err := net.Dial("tcp", serverAddr) |
||||||
|
if err != nil { |
||||||
|
t.Fatal("dialing", err) |
||||||
|
} |
||||||
|
|
||||||
|
config := newConfig(t, "/echo") |
||||||
|
config.Location, err = url.ParseRequestURI(fmt.Sprintf("ws://%s/echo?q=v", serverAddr)) |
||||||
|
if err != nil { |
||||||
|
t.Fatal("location url", err) |
||||||
|
} |
||||||
|
|
||||||
|
ws, err := NewClient(config, client) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("WebSocket handshake: %v", err) |
||||||
|
return |
||||||
|
} |
||||||
|
ws.Close() |
||||||
|
} |
||||||
|
|
||||||
|
func testWithProtocol(t *testing.T, subproto []string) (string, error) { |
||||||
|
once.Do(startServer) |
||||||
|
|
||||||
|
client, err := net.Dial("tcp", serverAddr) |
||||||
|
if err != nil { |
||||||
|
t.Fatal("dialing", err) |
||||||
|
} |
||||||
|
|
||||||
|
config := newConfig(t, "/subproto") |
||||||
|
config.Protocol = subproto |
||||||
|
|
||||||
|
ws, err := NewClient(config, client) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
msg := make([]byte, 16) |
||||||
|
n, err := ws.Read(msg) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
ws.Close() |
||||||
|
return string(msg[:n]), nil |
||||||
|
} |
||||||
|
|
||||||
|
func TestWithProtocol(t *testing.T) { |
||||||
|
proto, err := testWithProtocol(t, []string{"chat"}) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("SubProto: unexpected error: %v", err) |
||||||
|
} |
||||||
|
if proto != "chat" { |
||||||
|
t.Errorf("SubProto: expected %q, got %q", "chat", proto) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestWithTwoProtocol(t *testing.T) { |
||||||
|
proto, err := testWithProtocol(t, []string{"test", "chat"}) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("SubProto: unexpected error: %v", err) |
||||||
|
} |
||||||
|
if proto != "chat" { |
||||||
|
t.Errorf("SubProto: expected %q, got %q", "chat", proto) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestWithBadProtocol(t *testing.T) { |
||||||
|
_, err := testWithProtocol(t, []string{"test"}) |
||||||
|
if err != ErrBadStatus { |
||||||
|
t.Errorf("SubProto: expected %v, got %v", ErrBadStatus, err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestHTTP(t *testing.T) { |
||||||
|
once.Do(startServer) |
||||||
|
|
||||||
|
// If the client did not send a handshake that matches the protocol
|
||||||
|
// specification, the server MUST return an HTTP response with an
|
||||||
|
// appropriate error code (such as 400 Bad Request)
|
||||||
|
resp, err := http.Get(fmt.Sprintf("http://%s/echo", serverAddr)) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("Get: error %#v", err) |
||||||
|
return |
||||||
|
} |
||||||
|
if resp == nil { |
||||||
|
t.Error("Get: resp is null") |
||||||
|
return |
||||||
|
} |
||||||
|
if resp.StatusCode != http.StatusBadRequest { |
||||||
|
t.Errorf("Get: expected %q got %q", http.StatusBadRequest, resp.StatusCode) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestTrailingSpaces(t *testing.T) { |
||||||
|
// http://code.google.com/p/go/issues/detail?id=955
|
||||||
|
// The last runs of this create keys with trailing spaces that should not be
|
||||||
|
// generated by the client.
|
||||||
|
once.Do(startServer) |
||||||
|
config := newConfig(t, "/echo") |
||||||
|
for i := 0; i < 30; i++ { |
||||||
|
// body
|
||||||
|
ws, err := DialConfig(config) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("Dial #%d failed: %v", i, err) |
||||||
|
break |
||||||
|
} |
||||||
|
ws.Close() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestDialConfigBadVersion(t *testing.T) { |
||||||
|
once.Do(startServer) |
||||||
|
config := newConfig(t, "/echo") |
||||||
|
config.Version = 1234 |
||||||
|
|
||||||
|
_, err := DialConfig(config) |
||||||
|
|
||||||
|
if dialerr, ok := err.(*DialError); ok { |
||||||
|
if dialerr.Err != ErrBadProtocolVersion { |
||||||
|
t.Errorf("dial expected err %q but got %q", ErrBadProtocolVersion, dialerr.Err) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestSmallBuffer(t *testing.T) { |
||||||
|
// http://code.google.com/p/go/issues/detail?id=1145
|
||||||
|
// Read should be able to handle reading a fragment of a frame.
|
||||||
|
once.Do(startServer) |
||||||
|
|
||||||
|
// websocket.Dial()
|
||||||
|
client, err := net.Dial("tcp", serverAddr) |
||||||
|
if err != nil { |
||||||
|
t.Fatal("dialing", err) |
||||||
|
} |
||||||
|
conn, err := NewClient(newConfig(t, "/echo"), client) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("WebSocket handshake error: %v", err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
msg := []byte("hello, world\n") |
||||||
|
if _, err := conn.Write(msg); err != nil { |
||||||
|
t.Errorf("Write: %v", err) |
||||||
|
} |
||||||
|
var small_msg = make([]byte, 8) |
||||||
|
n, err := conn.Read(small_msg) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("Read: %v", err) |
||||||
|
} |
||||||
|
if !bytes.Equal(msg[:len(small_msg)], small_msg) { |
||||||
|
t.Errorf("Echo: expected %q got %q", msg[:len(small_msg)], small_msg) |
||||||
|
} |
||||||
|
var second_msg = make([]byte, len(msg)) |
||||||
|
n, err = conn.Read(second_msg) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("Read: %v", err) |
||||||
|
} |
||||||
|
second_msg = second_msg[0:n] |
||||||
|
if !bytes.Equal(msg[len(small_msg):], second_msg) { |
||||||
|
t.Errorf("Echo: expected %q got %q", msg[len(small_msg):], second_msg) |
||||||
|
} |
||||||
|
conn.Close() |
||||||
|
} |
||||||
|
|
||||||
|
var parseAuthorityTests = []struct { |
||||||
|
in *url.URL |
||||||
|
out string |
||||||
|
}{ |
||||||
|
{ |
||||||
|
&url.URL{ |
||||||
|
Scheme: "ws", |
||||||
|
Host: "www.google.com", |
||||||
|
}, |
||||||
|
"www.google.com:80", |
||||||
|
}, |
||||||
|
{ |
||||||
|
&url.URL{ |
||||||
|
Scheme: "wss", |
||||||
|
Host: "www.google.com", |
||||||
|
}, |
||||||
|
"www.google.com:443", |
||||||
|
}, |
||||||
|
{ |
||||||
|
&url.URL{ |
||||||
|
Scheme: "ws", |
||||||
|
Host: "www.google.com:80", |
||||||
|
}, |
||||||
|
"www.google.com:80", |
||||||
|
}, |
||||||
|
{ |
||||||
|
&url.URL{ |
||||||
|
Scheme: "wss", |
||||||
|
Host: "www.google.com:443", |
||||||
|
}, |
||||||
|
"www.google.com:443", |
||||||
|
}, |
||||||
|
// some invalid ones for parseAuthority. parseAuthority doesn't
|
||||||
|
// concern itself with the scheme unless it actually knows about it
|
||||||
|
{ |
||||||
|
&url.URL{ |
||||||
|
Scheme: "http", |
||||||
|
Host: "www.google.com", |
||||||
|
}, |
||||||
|
"www.google.com", |
||||||
|
}, |
||||||
|
{ |
||||||
|
&url.URL{ |
||||||
|
Scheme: "http", |
||||||
|
Host: "www.google.com:80", |
||||||
|
}, |
||||||
|
"www.google.com:80", |
||||||
|
}, |
||||||
|
{ |
||||||
|
&url.URL{ |
||||||
|
Scheme: "asdf", |
||||||
|
Host: "127.0.0.1", |
||||||
|
}, |
||||||
|
"127.0.0.1", |
||||||
|
}, |
||||||
|
{ |
||||||
|
&url.URL{ |
||||||
|
Scheme: "asdf", |
||||||
|
Host: "www.google.com", |
||||||
|
}, |
||||||
|
"www.google.com", |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
func TestParseAuthority(t *testing.T) { |
||||||
|
for _, tt := range parseAuthorityTests { |
||||||
|
out := parseAuthority(tt.in) |
||||||
|
if out != tt.out { |
||||||
|
t.Errorf("got %v; want %v", out, tt.out) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,74 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package utils |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/crypto" |
|
||||||
"github.com/ethereum/go-ethereum/node" |
|
||||||
"github.com/ethereum/go-ethereum/p2p" |
|
||||||
rpc "github.com/ethereum/go-ethereum/rpc/v2" |
|
||||||
) |
|
||||||
|
|
||||||
// PublicWeb3API offers helper utils
|
|
||||||
type PublicWeb3API struct { |
|
||||||
stack *node.Node |
|
||||||
} |
|
||||||
|
|
||||||
// NewPublicWeb3API creates a new Web3Service instance
|
|
||||||
func NewPublicWeb3API(stack *node.Node) *PublicWeb3API { |
|
||||||
return &PublicWeb3API{stack} |
|
||||||
} |
|
||||||
|
|
||||||
// ClientVersion returns the node name
|
|
||||||
func (s *PublicWeb3API) ClientVersion() string { |
|
||||||
return s.stack.Server().Name |
|
||||||
} |
|
||||||
|
|
||||||
// Sha3 applies the ethereum sha3 implementation on the input.
|
|
||||||
// It assumes the input is hex encoded.
|
|
||||||
func (s *PublicWeb3API) Sha3(input string) string { |
|
||||||
return common.ToHex(crypto.Sha3(common.FromHex(input))) |
|
||||||
} |
|
||||||
|
|
||||||
// PublicNetAPI offers network related RPC methods
|
|
||||||
type PublicNetAPI struct { |
|
||||||
net *p2p.Server |
|
||||||
networkVersion int |
|
||||||
} |
|
||||||
|
|
||||||
// NewPublicNetAPI creates a new net api instance.
|
|
||||||
func NewPublicNetAPI(net *p2p.Server, networkVersion int) *PublicNetAPI { |
|
||||||
return &PublicNetAPI{net, networkVersion} |
|
||||||
} |
|
||||||
|
|
||||||
// Listening returns an indication if the node is listening for network connections.
|
|
||||||
func (s *PublicNetAPI) Listening() bool { |
|
||||||
return true // always listening
|
|
||||||
} |
|
||||||
|
|
||||||
// Peercount returns the number of connected peers
|
|
||||||
func (s *PublicNetAPI) PeerCount() *rpc.HexNumber { |
|
||||||
return rpc.NewHexNumber(s.net.PeerCount()) |
|
||||||
} |
|
||||||
|
|
||||||
// ProtocolVersion returns the current ethereum protocol version.
|
|
||||||
func (s *PublicNetAPI) Version() string { |
|
||||||
return fmt.Sprintf("%d", s.networkVersion) |
|
||||||
} |
|
@ -0,0 +1,176 @@ |
|||||||
|
// Copyright 2015 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package utils |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
|
||||||
|
"strings" |
||||||
|
|
||||||
|
"github.com/codegangsta/cli" |
||||||
|
"github.com/ethereum/go-ethereum/eth" |
||||||
|
"github.com/ethereum/go-ethereum/logger" |
||||||
|
"github.com/ethereum/go-ethereum/logger/glog" |
||||||
|
"github.com/ethereum/go-ethereum/node" |
||||||
|
"github.com/ethereum/go-ethereum/rpc" |
||||||
|
) |
||||||
|
|
||||||
|
// NewInProcRPCClient will start a new RPC server for the given node and returns a client to interact with it.
|
||||||
|
func NewInProcRPCClient(stack *node.Node) *inProcClient { |
||||||
|
server := rpc.NewServer() |
||||||
|
|
||||||
|
offered := stack.APIs() |
||||||
|
for _, api := range offered { |
||||||
|
server.RegisterName(api.Namespace, api.Service) |
||||||
|
} |
||||||
|
|
||||||
|
web3 := node.NewPublicWeb3API(stack) |
||||||
|
server.RegisterName("web3", web3) |
||||||
|
|
||||||
|
var ethereum *eth.Ethereum |
||||||
|
if err := stack.Service(ðereum); err == nil { |
||||||
|
net := eth.NewPublicNetAPI(stack.Server(), ethereum.NetVersion()) |
||||||
|
server.RegisterName("net", net) |
||||||
|
} else { |
||||||
|
glog.V(logger.Warn).Infof("%v\n", err) |
||||||
|
} |
||||||
|
|
||||||
|
buf := &buf{ |
||||||
|
requests: make(chan []byte), |
||||||
|
responses: make(chan []byte), |
||||||
|
} |
||||||
|
client := &inProcClient{ |
||||||
|
server: server, |
||||||
|
buf: buf, |
||||||
|
} |
||||||
|
|
||||||
|
go func() { |
||||||
|
server.ServeCodec(rpc.NewJSONCodec(client.buf)) |
||||||
|
}() |
||||||
|
|
||||||
|
return client |
||||||
|
} |
||||||
|
|
||||||
|
// buf represents the connection between the RPC server and console
|
||||||
|
type buf struct { |
||||||
|
readBuf []byte // store remaining request bytes after a partial read
|
||||||
|
requests chan []byte // list with raw serialized requests
|
||||||
|
responses chan []byte // list with raw serialized responses
|
||||||
|
} |
||||||
|
|
||||||
|
// will read the next request in json format
|
||||||
|
func (b *buf) Read(p []byte) (int, error) { |
||||||
|
// last read didn't read entire request, return remaining bytes
|
||||||
|
if len(b.readBuf) > 0 { |
||||||
|
n := copy(p, b.readBuf) |
||||||
|
if n < len(b.readBuf) { |
||||||
|
b.readBuf = b.readBuf[:n] |
||||||
|
} else { |
||||||
|
b.readBuf = b.readBuf[:0] |
||||||
|
} |
||||||
|
return n, nil |
||||||
|
} |
||||||
|
|
||||||
|
// read next request
|
||||||
|
req := <-b.requests |
||||||
|
n := copy(p, req) |
||||||
|
if n < len(req) { |
||||||
|
// buf too small, store remaining chunk for next read
|
||||||
|
b.readBuf = req[n:] |
||||||
|
} |
||||||
|
|
||||||
|
return n, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Write send the given buffer to the backend
|
||||||
|
func (b *buf) Write(p []byte) (n int, err error) { |
||||||
|
b.responses <- p |
||||||
|
return len(p), nil |
||||||
|
} |
||||||
|
|
||||||
|
// Close cleans up obtained resources.
|
||||||
|
func (b *buf) Close() error { |
||||||
|
close(b.requests) |
||||||
|
close(b.responses) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// inProcClient starts a RPC server and uses buf to communicate with it.
|
||||||
|
type inProcClient struct { |
||||||
|
server *rpc.Server |
||||||
|
buf *buf |
||||||
|
} |
||||||
|
|
||||||
|
// Close will stop the RPC server
|
||||||
|
func (c *inProcClient) Close() { |
||||||
|
c.server.Stop() |
||||||
|
} |
||||||
|
|
||||||
|
// Send a msg to the endpoint
|
||||||
|
func (c *inProcClient) Send(msg interface{}) error { |
||||||
|
d, err := json.Marshal(msg) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
c.buf.requests <- d |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Recv reads a message and tries to parse it into the given msg
|
||||||
|
func (c *inProcClient) Recv(msg interface{}) error { |
||||||
|
data := <-c.buf.responses |
||||||
|
return json.Unmarshal(data, &msg) |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the collection of modules the RPC server offers.
|
||||||
|
func (c *inProcClient) SupportedModules() (map[string]string, error) { |
||||||
|
return rpc.SupportedModules(c) |
||||||
|
} |
||||||
|
|
||||||
|
// NewRemoteRPCClient returns a RPC client which connects to a running geth instance.
|
||||||
|
// Depending on the given context this can either be a IPC or a HTTP client.
|
||||||
|
func NewRemoteRPCClient(ctx *cli.Context) (rpc.Client, error) { |
||||||
|
if ctx.Args().Present() { |
||||||
|
endpoint := ctx.Args().First() |
||||||
|
return NewRemoteRPCClientFromString(endpoint) |
||||||
|
} |
||||||
|
|
||||||
|
// use IPC by default
|
||||||
|
endpoint := IPCSocketPath(ctx) |
||||||
|
return rpc.NewIPCClient(endpoint) |
||||||
|
} |
||||||
|
|
||||||
|
// NewRemoteRPCClientFromString returns a RPC client which connects to the given
|
||||||
|
// endpoint. It must start with either `ipc:` or `rpc:` (HTTP).
|
||||||
|
func NewRemoteRPCClientFromString(endpoint string) (rpc.Client, error) { |
||||||
|
if strings.HasPrefix(endpoint, "ipc:") { |
||||||
|
return rpc.NewIPCClient(endpoint[4:]) |
||||||
|
} |
||||||
|
if strings.HasPrefix(endpoint, "rpc:") { |
||||||
|
return rpc.NewHTTPClient(endpoint[4:]) |
||||||
|
} |
||||||
|
if strings.HasPrefix(endpoint, "http://") { |
||||||
|
return rpc.NewHTTPClient(endpoint) |
||||||
|
} |
||||||
|
if strings.HasPrefix(endpoint, "ws:") { |
||||||
|
return rpc.NewWSClient(endpoint) |
||||||
|
} |
||||||
|
|
||||||
|
return nil, fmt.Errorf("invalid endpoint") |
||||||
|
} |
@ -0,0 +1,265 @@ |
|||||||
|
// Copyright 2015 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package ethreg |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts" |
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/compiler" |
||||||
|
"github.com/ethereum/go-ethereum/core" |
||||||
|
"github.com/ethereum/go-ethereum/core/state" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"github.com/ethereum/go-ethereum/ethdb" |
||||||
|
"github.com/ethereum/go-ethereum/logger" |
||||||
|
"github.com/ethereum/go-ethereum/logger/glog" |
||||||
|
"github.com/ethereum/go-ethereum/common/registrar" |
||||||
|
) |
||||||
|
|
||||||
|
// registryAPIBackend is a backend for an Ethereum Registry.
|
||||||
|
type registryAPIBackend struct { |
||||||
|
bc *core.BlockChain |
||||||
|
chainDb ethdb.Database |
||||||
|
txPool *core.TxPool |
||||||
|
am *accounts.Manager |
||||||
|
} |
||||||
|
|
||||||
|
// PrivateRegistarAPI offers various functions to access the Ethereum registry.
|
||||||
|
type PrivateRegistarAPI struct { |
||||||
|
be *registryAPIBackend |
||||||
|
} |
||||||
|
|
||||||
|
// NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance.
|
||||||
|
func NewPrivateRegistarAPI(bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { |
||||||
|
return &PrivateRegistarAPI{®istryAPIBackend{bc, chainDb, txPool, am}} |
||||||
|
} |
||||||
|
|
||||||
|
// SetGlobalRegistrar allows clients to set the global registry for the node.
|
||||||
|
// This method can be used to deploy a new registry. First zero out the current
|
||||||
|
// address by calling the method with namereg = '0x0' and then call this method
|
||||||
|
// again with '' as namereg. This will submit a transaction to the network which
|
||||||
|
// will deploy a new registry on execution. The TX hash is returned. When called
|
||||||
|
// with namereg '' and the current address is not zero the current global is
|
||||||
|
// address is returned..
|
||||||
|
func (api *PrivateRegistarAPI) SetGlobalRegistrar(namereg string, from common.Address) (string, error) { |
||||||
|
return registrar.New(api.be).SetGlobalRegistrar(namereg, from) |
||||||
|
} |
||||||
|
|
||||||
|
// SetHashReg queries the registry for a hash.
|
||||||
|
func (api *PrivateRegistarAPI) SetHashReg(hashreg string, from common.Address) (string, error) { |
||||||
|
return registrar.New(api.be).SetHashReg(hashreg, from) |
||||||
|
} |
||||||
|
|
||||||
|
// SetUrlHint queries the registry for an url.
|
||||||
|
func (api *PrivateRegistarAPI) SetUrlHint(hashreg string, from common.Address) (string, error) { |
||||||
|
return registrar.New(api.be).SetUrlHint(hashreg, from) |
||||||
|
} |
||||||
|
|
||||||
|
// SaveInfo stores contract information on the local file system.
|
||||||
|
func (api *PrivateRegistarAPI) SaveInfo(info *compiler.ContractInfo, filename string) (contenthash common.Hash, err error) { |
||||||
|
return compiler.SaveInfo(info, filename) |
||||||
|
} |
||||||
|
|
||||||
|
// Register registers a new content hash in the registry.
|
||||||
|
func (api *PrivateRegistarAPI) Register(sender common.Address, addr common.Address, contentHashHex string) (bool, error) { |
||||||
|
block := api.be.bc.CurrentBlock() |
||||||
|
state, err := state.New(block.Root(), api.be.chainDb) |
||||||
|
if err != nil { |
||||||
|
return false, err |
||||||
|
} |
||||||
|
|
||||||
|
codeb := state.GetCode(addr) |
||||||
|
codeHash := common.BytesToHash(crypto.Sha3(codeb)) |
||||||
|
contentHash := common.HexToHash(contentHashHex) |
||||||
|
|
||||||
|
_, err = registrar.New(api.be).SetHashToHash(sender, codeHash, contentHash) |
||||||
|
return err == nil, err |
||||||
|
} |
||||||
|
|
||||||
|
// RegisterUrl registers a new url in the registry.
|
||||||
|
func (api *PrivateRegistarAPI) RegisterUrl(sender common.Address, contentHashHex string, url string) (bool, error) { |
||||||
|
_, err := registrar.New(api.be).SetUrlToHash(sender, common.HexToHash(contentHashHex), url) |
||||||
|
return err == nil, err |
||||||
|
} |
||||||
|
|
||||||
|
// callmsg is the message type used for call transations.
|
||||||
|
type callmsg struct { |
||||||
|
from *state.StateObject |
||||||
|
to *common.Address |
||||||
|
gas, gasPrice *big.Int |
||||||
|
value *big.Int |
||||||
|
data []byte |
||||||
|
} |
||||||
|
|
||||||
|
// accessor boilerplate to implement core.Message
|
||||||
|
func (m callmsg) From() (common.Address, error) { |
||||||
|
return m.from.Address(), nil |
||||||
|
} |
||||||
|
func (m callmsg) Nonce() uint64 { |
||||||
|
return m.from.Nonce() |
||||||
|
} |
||||||
|
func (m callmsg) To() *common.Address { |
||||||
|
return m.to |
||||||
|
} |
||||||
|
func (m callmsg) GasPrice() *big.Int { |
||||||
|
return m.gasPrice |
||||||
|
} |
||||||
|
func (m callmsg) Gas() *big.Int { |
||||||
|
return m.gas |
||||||
|
} |
||||||
|
func (m callmsg) Value() *big.Int { |
||||||
|
return m.value |
||||||
|
} |
||||||
|
func (m callmsg) Data() []byte { |
||||||
|
return m.data |
||||||
|
} |
||||||
|
|
||||||
|
// Call forms a transaction from the given arguments and tries to execute it on
|
||||||
|
// a private VM with a copy of the state. Any changes are therefore only temporary
|
||||||
|
// and not part of the actual state. This allows for local execution/queries.
|
||||||
|
func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) { |
||||||
|
block := be.bc.CurrentBlock() |
||||||
|
statedb, err := state.New(block.Root(), be.chainDb) |
||||||
|
if err != nil { |
||||||
|
return "", "", err |
||||||
|
} |
||||||
|
|
||||||
|
var from *state.StateObject |
||||||
|
if len(fromStr) == 0 { |
||||||
|
accounts, err := be.am.Accounts() |
||||||
|
if err != nil || len(accounts) == 0 { |
||||||
|
from = statedb.GetOrNewStateObject(common.Address{}) |
||||||
|
} else { |
||||||
|
from = statedb.GetOrNewStateObject(accounts[0].Address) |
||||||
|
} |
||||||
|
} else { |
||||||
|
from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr)) |
||||||
|
} |
||||||
|
|
||||||
|
from.SetBalance(common.MaxBig) |
||||||
|
|
||||||
|
msg := callmsg{ |
||||||
|
from: from, |
||||||
|
gas: common.Big(gasStr), |
||||||
|
gasPrice: common.Big(gasPriceStr), |
||||||
|
value: common.Big(valueStr), |
||||||
|
data: common.FromHex(dataStr), |
||||||
|
} |
||||||
|
if len(toStr) > 0 { |
||||||
|
addr := common.HexToAddress(toStr) |
||||||
|
msg.to = &addr |
||||||
|
} |
||||||
|
|
||||||
|
if msg.gas.Cmp(big.NewInt(0)) == 0 { |
||||||
|
msg.gas = big.NewInt(50000000) |
||||||
|
} |
||||||
|
|
||||||
|
if msg.gasPrice.Cmp(big.NewInt(0)) == 0 { |
||||||
|
msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) |
||||||
|
} |
||||||
|
|
||||||
|
header := be.bc.CurrentBlock().Header() |
||||||
|
vmenv := core.NewEnv(statedb, be.bc, msg, header) |
||||||
|
gp := new(core.GasPool).AddGas(common.MaxBig) |
||||||
|
res, gas, err := core.ApplyMessage(vmenv, msg, gp) |
||||||
|
|
||||||
|
return common.ToHex(res), gas.String(), err |
||||||
|
} |
||||||
|
|
||||||
|
// StorageAt returns the data stores in the state for the given address and location.
|
||||||
|
func (be *registryAPIBackend) StorageAt(addr string, storageAddr string) string { |
||||||
|
block := be.bc.CurrentBlock() |
||||||
|
state, err := state.New(block.Root(), be.chainDb) |
||||||
|
if err != nil { |
||||||
|
return "" |
||||||
|
} |
||||||
|
return state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex() |
||||||
|
} |
||||||
|
|
||||||
|
// Transact forms a transaction from the given arguments and submits it to the
|
||||||
|
// transactio pool for execution.
|
||||||
|
func (be *registryAPIBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { |
||||||
|
if len(toStr) > 0 && toStr != "0x" && !common.IsHexAddress(toStr) { |
||||||
|
return "", errors.New("invalid address") |
||||||
|
} |
||||||
|
|
||||||
|
var ( |
||||||
|
from = common.HexToAddress(fromStr) |
||||||
|
to = common.HexToAddress(toStr) |
||||||
|
value = common.Big(valueStr) |
||||||
|
gas *big.Int |
||||||
|
price *big.Int |
||||||
|
data []byte |
||||||
|
contractCreation bool |
||||||
|
) |
||||||
|
|
||||||
|
if len(gasStr) == 0 { |
||||||
|
gas = big.NewInt(90000) |
||||||
|
} else { |
||||||
|
gas = common.Big(gasStr) |
||||||
|
} |
||||||
|
|
||||||
|
if len(gasPriceStr) == 0 { |
||||||
|
price = big.NewInt(10000000000000) |
||||||
|
} else { |
||||||
|
price = common.Big(gasPriceStr) |
||||||
|
} |
||||||
|
|
||||||
|
data = common.FromHex(codeStr) |
||||||
|
if len(toStr) == 0 { |
||||||
|
contractCreation = true |
||||||
|
} |
||||||
|
|
||||||
|
nonce := be.txPool.State().GetNonce(from) |
||||||
|
if len(nonceStr) != 0 { |
||||||
|
nonce = common.Big(nonceStr).Uint64() |
||||||
|
} |
||||||
|
|
||||||
|
var tx *types.Transaction |
||||||
|
if contractCreation { |
||||||
|
tx = types.NewContractCreation(nonce, value, gas, price, data) |
||||||
|
} else { |
||||||
|
tx = types.NewTransaction(nonce, to, value, gas, price, data) |
||||||
|
} |
||||||
|
|
||||||
|
acc := accounts.Account{from} |
||||||
|
signature, err := be.am.Sign(acc, tx.SigHash().Bytes()) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
signedTx, err := tx.WithSignature(signature) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
|
||||||
|
be.txPool.SetLocal(signedTx) |
||||||
|
if err := be.txPool.Add(signedTx); err != nil { |
||||||
|
return "", nil |
||||||
|
} |
||||||
|
|
||||||
|
if contractCreation { |
||||||
|
addr := crypto.CreateAddress(from, nonce) |
||||||
|
glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex()) |
||||||
|
} else { |
||||||
|
glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex()) |
||||||
|
} |
||||||
|
|
||||||
|
return signedTx.Hash().Hex(), nil |
||||||
|
} |
@ -1,48 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package ethreg |
|
||||||
|
|
||||||
import ( |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/registrar" |
|
||||||
"github.com/ethereum/go-ethereum/xeth" |
|
||||||
) |
|
||||||
|
|
||||||
// implements a versioned Registrar on an archiving full node
|
|
||||||
type EthReg struct { |
|
||||||
backend *xeth.XEth |
|
||||||
registry *registrar.Registrar |
|
||||||
} |
|
||||||
|
|
||||||
func New(xe *xeth.XEth) (self *EthReg) { |
|
||||||
self = &EthReg{backend: xe} |
|
||||||
self.registry = registrar.New(xe) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func (self *EthReg) Registry() *registrar.Registrar { |
|
||||||
return self.registry |
|
||||||
} |
|
||||||
|
|
||||||
func (self *EthReg) Resolver(n *big.Int) *registrar.Registrar { |
|
||||||
xe := self.backend |
|
||||||
if n != nil { |
|
||||||
xe = self.backend.AtStateNum(n.Int64()) |
|
||||||
} |
|
||||||
return registrar.New(xe) |
|
||||||
} |
|
@ -1,465 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"io" |
|
||||||
"math/big" |
|
||||||
"os" |
|
||||||
"time" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/common/compiler" |
|
||||||
"github.com/ethereum/go-ethereum/common/natspec" |
|
||||||
"github.com/ethereum/go-ethereum/common/registrar" |
|
||||||
"github.com/ethereum/go-ethereum/core" |
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
"github.com/ethereum/go-ethereum/crypto" |
|
||||||
"github.com/ethereum/go-ethereum/eth" |
|
||||||
"github.com/ethereum/go-ethereum/node" |
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover" |
|
||||||
"github.com/ethereum/go-ethereum/rlp" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/comms" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/useragent" |
|
||||||
"github.com/ethereum/go-ethereum/xeth" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
AdminApiversion = "1.0" |
|
||||||
importBatchSize = 2500 |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// mapping between methods and handlers
|
|
||||||
AdminMapping = map[string]adminhandler{ |
|
||||||
"admin_addPeer": (*adminApi).AddPeer, |
|
||||||
"admin_peers": (*adminApi).Peers, |
|
||||||
"admin_nodeInfo": (*adminApi).NodeInfo, |
|
||||||
"admin_exportChain": (*adminApi).ExportChain, |
|
||||||
"admin_importChain": (*adminApi).ImportChain, |
|
||||||
"admin_setSolc": (*adminApi).SetSolc, |
|
||||||
"admin_datadir": (*adminApi).DataDir, |
|
||||||
"admin_startRPC": (*adminApi).StartRPC, |
|
||||||
"admin_stopRPC": (*adminApi).StopRPC, |
|
||||||
"admin_setGlobalRegistrar": (*adminApi).SetGlobalRegistrar, |
|
||||||
"admin_setHashReg": (*adminApi).SetHashReg, |
|
||||||
"admin_setUrlHint": (*adminApi).SetUrlHint, |
|
||||||
"admin_saveInfo": (*adminApi).SaveInfo, |
|
||||||
"admin_register": (*adminApi).Register, |
|
||||||
"admin_registerUrl": (*adminApi).RegisterUrl, |
|
||||||
"admin_startNatSpec": (*adminApi).StartNatSpec, |
|
||||||
"admin_stopNatSpec": (*adminApi).StopNatSpec, |
|
||||||
"admin_getContractInfo": (*adminApi).GetContractInfo, |
|
||||||
"admin_httpGet": (*adminApi).HttpGet, |
|
||||||
"admin_sleepBlocks": (*adminApi).SleepBlocks, |
|
||||||
"admin_sleep": (*adminApi).Sleep, |
|
||||||
"admin_enableUserAgent": (*adminApi).EnableUserAgent, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
// admin callback handler
|
|
||||||
type adminhandler func(*adminApi, *shared.Request) (interface{}, error) |
|
||||||
|
|
||||||
// admin api provider
|
|
||||||
type adminApi struct { |
|
||||||
xeth *xeth.XEth |
|
||||||
stack *node.Node |
|
||||||
ethereum *eth.Ethereum |
|
||||||
codec codec.Codec |
|
||||||
coder codec.ApiCoder |
|
||||||
} |
|
||||||
|
|
||||||
// create a new admin api instance
|
|
||||||
func NewAdminApi(xeth *xeth.XEth, stack *node.Node, codec codec.Codec) *adminApi { |
|
||||||
api := &adminApi{ |
|
||||||
xeth: xeth, |
|
||||||
stack: stack, |
|
||||||
codec: codec, |
|
||||||
coder: codec.New(nil), |
|
||||||
} |
|
||||||
if stack != nil { |
|
||||||
stack.Service(&api.ethereum) |
|
||||||
} |
|
||||||
return api |
|
||||||
} |
|
||||||
|
|
||||||
// collection with supported methods
|
|
||||||
func (self *adminApi) Methods() []string { |
|
||||||
methods := make([]string, len(AdminMapping)) |
|
||||||
i := 0 |
|
||||||
for k := range AdminMapping { |
|
||||||
methods[i] = k |
|
||||||
i++ |
|
||||||
} |
|
||||||
return methods |
|
||||||
} |
|
||||||
|
|
||||||
// Execute given request
|
|
||||||
func (self *adminApi) Execute(req *shared.Request) (interface{}, error) { |
|
||||||
if callback, ok := AdminMapping[req.Method]; ok { |
|
||||||
return callback(self, req) |
|
||||||
} |
|
||||||
|
|
||||||
return nil, &shared.NotImplementedError{req.Method} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) Name() string { |
|
||||||
return shared.AdminApiName |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) ApiVersion() string { |
|
||||||
return AdminApiversion |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(AddPeerArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
node, err := discover.ParseNode(args.Url) |
|
||||||
if err != nil { |
|
||||||
return nil, fmt.Errorf("invalid node URL: %v", err) |
|
||||||
} |
|
||||||
self.stack.Server().AddPeer(node) |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) Peers(req *shared.Request) (interface{}, error) { |
|
||||||
return self.stack.Server().PeersInfo(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) NodeInfo(req *shared.Request) (interface{}, error) { |
|
||||||
return self.stack.Server().NodeInfo(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) DataDir(req *shared.Request) (interface{}, error) { |
|
||||||
return self.stack.DataDir(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool { |
|
||||||
for _, b := range bs { |
|
||||||
if !chain.HasBlock(b.Hash()) { |
|
||||||
return false |
|
||||||
} |
|
||||||
} |
|
||||||
return true |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) ImportChain(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(ImportExportChainArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
fh, err := os.Open(args.Filename) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
defer fh.Close() |
|
||||||
stream := rlp.NewStream(fh, 0) |
|
||||||
|
|
||||||
// Run actual the import.
|
|
||||||
blocks := make(types.Blocks, importBatchSize) |
|
||||||
n := 0 |
|
||||||
for batch := 0; ; batch++ { |
|
||||||
|
|
||||||
i := 0 |
|
||||||
for ; i < importBatchSize; i++ { |
|
||||||
var b types.Block |
|
||||||
if err := stream.Decode(&b); err == io.EOF { |
|
||||||
break |
|
||||||
} else if err != nil { |
|
||||||
return false, fmt.Errorf("at block %d: %v", n, err) |
|
||||||
} |
|
||||||
blocks[i] = &b |
|
||||||
n++ |
|
||||||
} |
|
||||||
if i == 0 { |
|
||||||
break |
|
||||||
} |
|
||||||
// Import the batch.
|
|
||||||
if hasAllBlocks(self.ethereum.BlockChain(), blocks[:i]) { |
|
||||||
continue |
|
||||||
} |
|
||||||
if _, err := self.ethereum.BlockChain().InsertChain(blocks[:i]); err != nil { |
|
||||||
return false, fmt.Errorf("invalid block %d: %v", n, err) |
|
||||||
} |
|
||||||
} |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) ExportChain(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(ImportExportChainArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
fh, err := os.OpenFile(args.Filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
defer fh.Close() |
|
||||||
if err := self.ethereum.BlockChain().Export(fh); err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(SetSolcArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
solc, err := self.xeth.SetSolc(args.Path) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return solc.Info(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) StartRPC(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(StartRPCArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
cfg := comms.HttpConfig{ |
|
||||||
ListenAddress: args.ListenAddress, |
|
||||||
ListenPort: args.ListenPort, |
|
||||||
CorsDomain: args.CorsDomain, |
|
||||||
} |
|
||||||
|
|
||||||
apis, err := ParseApiString(args.Apis, self.codec, self.xeth, self.stack) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
err = comms.StartHttp(cfg, self.codec, Merge(apis...)) |
|
||||||
if err == nil { |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) StopRPC(req *shared.Request) (interface{}, error) { |
|
||||||
comms.StopHttp() |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) SleepBlocks(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(SleepBlocksArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
var timer <-chan time.Time |
|
||||||
var height *big.Int |
|
||||||
var err error |
|
||||||
if args.Timeout > 0 { |
|
||||||
timer = time.NewTimer(time.Duration(args.Timeout) * time.Second).C |
|
||||||
} |
|
||||||
|
|
||||||
height = new(big.Int).Add(self.xeth.CurrentBlock().Number(), big.NewInt(args.N)) |
|
||||||
height, err = sleepBlocks(self.xeth.UpdateState(), height, timer) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return height.Uint64(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func sleepBlocks(wait chan *big.Int, height *big.Int, timer <-chan time.Time) (newHeight *big.Int, err error) { |
|
||||||
wait <- height |
|
||||||
select { |
|
||||||
case <-timer: |
|
||||||
// if times out make sure the xeth loop does not block
|
|
||||||
go func() { |
|
||||||
select { |
|
||||||
case wait <- nil: |
|
||||||
case <-wait: |
|
||||||
} |
|
||||||
}() |
|
||||||
return nil, fmt.Errorf("timeout") |
|
||||||
case newHeight = <-wait: |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) Sleep(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(SleepArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
time.Sleep(time.Duration(args.S) * time.Second) |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) SetGlobalRegistrar(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(SetGlobalRegistrarArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
sender := common.HexToAddress(args.ContractAddress) |
|
||||||
|
|
||||||
reg := registrar.New(self.xeth) |
|
||||||
txhash, err := reg.SetGlobalRegistrar(args.NameReg, sender) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
return txhash, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) SetHashReg(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(SetHashRegArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
reg := registrar.New(self.xeth) |
|
||||||
sender := common.HexToAddress(args.Sender) |
|
||||||
txhash, err := reg.SetHashReg(args.HashReg, sender) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
return txhash, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) SetUrlHint(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(SetUrlHintArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
urlHint := args.UrlHint |
|
||||||
sender := common.HexToAddress(args.Sender) |
|
||||||
|
|
||||||
reg := registrar.New(self.xeth) |
|
||||||
txhash, err := reg.SetUrlHint(urlHint, sender) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return txhash, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) SaveInfo(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(SaveInfoArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
contenthash, err := compiler.SaveInfo(&args.ContractInfo, args.Filename) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return contenthash.Hex(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) Register(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(RegisterArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
sender := common.HexToAddress(args.Sender) |
|
||||||
// sender and contract address are passed as hex strings
|
|
||||||
codeb := self.xeth.CodeAtBytes(args.Address) |
|
||||||
codeHash := common.BytesToHash(crypto.Sha3(codeb)) |
|
||||||
contentHash := common.HexToHash(args.ContentHashHex) |
|
||||||
registry := registrar.New(self.xeth) |
|
||||||
|
|
||||||
_, err := registry.SetHashToHash(sender, codeHash, contentHash) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) RegisterUrl(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(RegisterUrlArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
sender := common.HexToAddress(args.Sender) |
|
||||||
registry := registrar.New(self.xeth) |
|
||||||
_, err := registry.SetUrlToHash(sender, common.HexToHash(args.ContentHash), args.Url) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) StartNatSpec(req *shared.Request) (interface{}, error) { |
|
||||||
self.ethereum.NatSpec = true |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) StopNatSpec(req *shared.Request) (interface{}, error) { |
|
||||||
self.ethereum.NatSpec = false |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) GetContractInfo(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(GetContractInfoArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
infoDoc, err := natspec.FetchDocsForContract(args.Contract, self.xeth, self.ethereum.HTTPClient()) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
var info interface{} |
|
||||||
err = self.coder.Decode(infoDoc, &info) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return info, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) HttpGet(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(HttpGetArgs) |
|
||||||
if err := self.coder.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
resp, err := self.ethereum.HTTPClient().Get(args.Uri, args.Path) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return string(resp), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *adminApi) EnableUserAgent(req *shared.Request) (interface{}, error) { |
|
||||||
if fe, ok := self.xeth.Frontend().(*useragent.RemoteFrontend); ok { |
|
||||||
fe.Enable() |
|
||||||
} |
|
||||||
return true, nil |
|
||||||
} |
|
@ -1,468 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/compiler" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
type AddPeerArgs struct { |
|
||||||
Url string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *AddPeerArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) != 1 { |
|
||||||
return shared.NewDecodeParamError("Expected enode as argument") |
|
||||||
} |
|
||||||
|
|
||||||
urlstr, ok := obj[0].(string) |
|
||||||
if !ok { |
|
||||||
return shared.NewInvalidTypeError("url", "not a string") |
|
||||||
} |
|
||||||
args.Url = urlstr |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type ImportExportChainArgs struct { |
|
||||||
Filename string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *ImportExportChainArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) != 1 { |
|
||||||
return shared.NewDecodeParamError("Expected filename as argument") |
|
||||||
} |
|
||||||
|
|
||||||
filename, ok := obj[0].(string) |
|
||||||
if !ok { |
|
||||||
return shared.NewInvalidTypeError("filename", "not a string") |
|
||||||
} |
|
||||||
args.Filename = filename |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type SetSolcArgs struct { |
|
||||||
Path string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *SetSolcArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) != 1 { |
|
||||||
return shared.NewDecodeParamError("Expected path as argument") |
|
||||||
} |
|
||||||
|
|
||||||
if pathstr, ok := obj[0].(string); ok { |
|
||||||
args.Path = pathstr |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
return shared.NewInvalidTypeError("path", "not a string") |
|
||||||
} |
|
||||||
|
|
||||||
type StartRPCArgs struct { |
|
||||||
ListenAddress string |
|
||||||
ListenPort uint |
|
||||||
CorsDomain string |
|
||||||
Apis string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *StartRPCArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
args.ListenAddress = "127.0.0.1" |
|
||||||
args.ListenPort = 8545 |
|
||||||
args.Apis = "net,eth,web3" |
|
||||||
|
|
||||||
if len(obj) >= 1 && obj[0] != nil { |
|
||||||
if addr, ok := obj[0].(string); ok { |
|
||||||
args.ListenAddress = addr |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("listenAddress", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 2 && obj[1] != nil { |
|
||||||
if port, ok := obj[1].(float64); ok && port >= 0 && port <= 64*1024 { |
|
||||||
args.ListenPort = uint(port) |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("listenPort", "not a valid port number") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 3 && obj[2] != nil { |
|
||||||
if corsDomain, ok := obj[2].(string); ok { |
|
||||||
args.CorsDomain = corsDomain |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("corsDomain", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 4 && obj[3] != nil { |
|
||||||
if apis, ok := obj[3].(string); ok { |
|
||||||
args.Apis = apis |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("apis", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type SleepArgs struct { |
|
||||||
S int |
|
||||||
} |
|
||||||
|
|
||||||
func (args *SleepArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
|
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
if len(obj) >= 1 { |
|
||||||
if obj[0] != nil { |
|
||||||
if n, err := numString(obj[0]); err == nil { |
|
||||||
args.S = int(n.Int64()) |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("N", "not an integer: "+err.Error()) |
|
||||||
} |
|
||||||
} else { |
|
||||||
return shared.NewInsufficientParamsError(0, 1) |
|
||||||
} |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type SleepBlocksArgs struct { |
|
||||||
N int64 |
|
||||||
Timeout int64 |
|
||||||
} |
|
||||||
|
|
||||||
func (args *SleepBlocksArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
|
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
args.N = 1 |
|
||||||
args.Timeout = 0 |
|
||||||
if len(obj) >= 1 && obj[0] != nil { |
|
||||||
if n, err := numString(obj[0]); err == nil { |
|
||||||
args.N = n.Int64() |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("N", "not an integer: "+err.Error()) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 2 && obj[1] != nil { |
|
||||||
if n, err := numString(obj[1]); err == nil { |
|
||||||
args.Timeout = n.Int64() |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("Timeout", "not an integer: "+err.Error()) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type SetGlobalRegistrarArgs struct { |
|
||||||
NameReg string |
|
||||||
ContractAddress string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *SetGlobalRegistrarArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) == 0 { |
|
||||||
return shared.NewDecodeParamError("Expected namereg address") |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 1 { |
|
||||||
if namereg, ok := obj[0].(string); ok { |
|
||||||
args.NameReg = namereg |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("NameReg", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 2 && obj[1] != nil { |
|
||||||
if addr, ok := obj[1].(string); ok { |
|
||||||
args.ContractAddress = addr |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("ContractAddress", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type SetHashRegArgs struct { |
|
||||||
HashReg string |
|
||||||
Sender string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *SetHashRegArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 1 && obj[0] != nil { |
|
||||||
if hashreg, ok := obj[0].(string); ok { |
|
||||||
args.HashReg = hashreg |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("HashReg", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 2 && obj[1] != nil { |
|
||||||
if sender, ok := obj[1].(string); ok { |
|
||||||
args.Sender = sender |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("Sender", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type SetUrlHintArgs struct { |
|
||||||
UrlHint string |
|
||||||
Sender string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *SetUrlHintArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 1 && obj[0] != nil { |
|
||||||
if urlhint, ok := obj[0].(string); ok { |
|
||||||
args.UrlHint = urlhint |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("UrlHint", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 2 && obj[1] != nil { |
|
||||||
if sender, ok := obj[1].(string); ok { |
|
||||||
args.Sender = sender |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("Sender", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type SaveInfoArgs struct { |
|
||||||
ContractInfo compiler.ContractInfo |
|
||||||
Filename string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *SaveInfoArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 2 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 2) |
|
||||||
} |
|
||||||
|
|
||||||
if jsonraw, err := json.Marshal(obj[0]); err == nil { |
|
||||||
if err = json.Unmarshal(jsonraw, &args.ContractInfo); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} else { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
if filename, ok := obj[1].(string); ok { |
|
||||||
args.Filename = filename |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("Filename", "not a string") |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type RegisterArgs struct { |
|
||||||
Sender string |
|
||||||
Address string |
|
||||||
ContentHashHex string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *RegisterArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 3 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 3) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 1 { |
|
||||||
if sender, ok := obj[0].(string); ok { |
|
||||||
args.Sender = sender |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("Sender", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 2 { |
|
||||||
if address, ok := obj[1].(string); ok { |
|
||||||
args.Address = address |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("Address", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 3 { |
|
||||||
if hex, ok := obj[2].(string); ok { |
|
||||||
args.ContentHashHex = hex |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("ContentHashHex", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type RegisterUrlArgs struct { |
|
||||||
Sender string |
|
||||||
ContentHash string |
|
||||||
Url string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *RegisterUrlArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 1 { |
|
||||||
if sender, ok := obj[0].(string); ok { |
|
||||||
args.Sender = sender |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("Sender", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 2 { |
|
||||||
if sender, ok := obj[1].(string); ok { |
|
||||||
args.ContentHash = sender |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("ContentHash", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 3 { |
|
||||||
if sender, ok := obj[2].(string); ok { |
|
||||||
args.Url = sender |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("Url", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type GetContractInfoArgs struct { |
|
||||||
Contract string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *GetContractInfoArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 1 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 1 { |
|
||||||
if contract, ok := obj[0].(string); ok { |
|
||||||
args.Contract = contract |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("Contract", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type HttpGetArgs struct { |
|
||||||
Uri string |
|
||||||
Path string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *HttpGetArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 1 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 1 { |
|
||||||
if uri, ok := obj[0].(string); ok { |
|
||||||
args.Uri = uri |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("Uri", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 2 && obj[1] != nil { |
|
||||||
if path, ok := obj[1].(string); ok { |
|
||||||
args.Path = path |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("Path", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
@ -1,143 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
const Admin_JS = ` |
|
||||||
web3._extend({ |
|
||||||
property: 'admin', |
|
||||||
methods: |
|
||||||
[ |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'addPeer', |
|
||||||
call: 'admin_addPeer', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'exportChain', |
|
||||||
call: 'admin_exportChain', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'importChain', |
|
||||||
call: 'admin_importChain', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'sleepBlocks', |
|
||||||
call: 'admin_sleepBlocks', |
|
||||||
params: 2, |
|
||||||
inputFormatter: [null, null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'setSolc', |
|
||||||
call: 'admin_setSolc', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'startRPC', |
|
||||||
call: 'admin_startRPC', |
|
||||||
params: 4, |
|
||||||
inputFormatter: [null, null, null, null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'stopRPC', |
|
||||||
call: 'admin_stopRPC', |
|
||||||
params: 0, |
|
||||||
inputFormatter: [] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'setGlobalRegistrar', |
|
||||||
call: 'admin_setGlobalRegistrar', |
|
||||||
params: 2, |
|
||||||
inputFormatter: [null,null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'setHashReg', |
|
||||||
call: 'admin_setHashReg', |
|
||||||
params: 2, |
|
||||||
inputFormatter: [null,null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'setUrlHint', |
|
||||||
call: 'admin_setUrlHint', |
|
||||||
params: 2, |
|
||||||
inputFormatter: [null,null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'saveInfo', |
|
||||||
call: 'admin_saveInfo', |
|
||||||
params: 2, |
|
||||||
inputFormatter: [null,null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'register', |
|
||||||
call: 'admin_register', |
|
||||||
params: 3, |
|
||||||
inputFormatter: [null,null,null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'registerUrl', |
|
||||||
call: 'admin_registerUrl', |
|
||||||
params: 3, |
|
||||||
inputFormatter: [null,null,null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'startNatSpec', |
|
||||||
call: 'admin_startNatSpec', |
|
||||||
params: 0, |
|
||||||
inputFormatter: [] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'stopNatSpec', |
|
||||||
call: 'admin_stopNatSpec', |
|
||||||
params: 0, |
|
||||||
inputFormatter: [] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'getContractInfo', |
|
||||||
call: 'admin_getContractInfo', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null], |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'httpGet', |
|
||||||
call: 'admin_httpGet', |
|
||||||
params: 2, |
|
||||||
inputFormatter: [null, null] |
|
||||||
}) |
|
||||||
], |
|
||||||
properties: |
|
||||||
[ |
|
||||||
new web3._extend.Property({ |
|
||||||
name: 'nodeInfo', |
|
||||||
getter: 'admin_nodeInfo' |
|
||||||
}), |
|
||||||
new web3._extend.Property({ |
|
||||||
name: 'peers', |
|
||||||
getter: 'admin_peers' |
|
||||||
}), |
|
||||||
new web3._extend.Property({ |
|
||||||
name: 'datadir', |
|
||||||
getter: 'admin_datadir' |
|
||||||
}) |
|
||||||
] |
|
||||||
}); |
|
||||||
` |
|
@ -1,26 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
// Merge multiple API's to a single API instance
|
|
||||||
func Merge(apis ...shared.EthereumApi) shared.EthereumApi { |
|
||||||
return newMergedApi(apis...) |
|
||||||
} |
|
@ -1,170 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"testing" |
|
||||||
|
|
||||||
"encoding/json" |
|
||||||
"strconv" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/compiler" |
|
||||||
"github.com/ethereum/go-ethereum/eth" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
"github.com/ethereum/go-ethereum/xeth" |
|
||||||
) |
|
||||||
|
|
||||||
func TestParseApiString(t *testing.T) { |
|
||||||
apis, err := ParseApiString("", codec.JSON, nil, nil) |
|
||||||
if err == nil { |
|
||||||
t.Errorf("Expected an err from parsing empty API string but got nil") |
|
||||||
} |
|
||||||
|
|
||||||
if len(apis) != 0 { |
|
||||||
t.Errorf("Expected 0 apis from empty API string") |
|
||||||
} |
|
||||||
|
|
||||||
apis, err = ParseApiString("eth", codec.JSON, nil, nil) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("Expected nil err from parsing empty API string but got %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
if len(apis) != 1 { |
|
||||||
t.Errorf("Expected 1 apis but got %d - %v", apis, apis) |
|
||||||
} |
|
||||||
|
|
||||||
apis, err = ParseApiString("eth,eth", codec.JSON, nil, nil) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("Expected nil err from parsing empty API string but got \"%v\"", err) |
|
||||||
} |
|
||||||
|
|
||||||
if len(apis) != 2 { |
|
||||||
t.Errorf("Expected 2 apis but got %d - %v", apis, apis) |
|
||||||
} |
|
||||||
|
|
||||||
apis, err = ParseApiString("eth,invalid", codec.JSON, nil, nil) |
|
||||||
if err == nil { |
|
||||||
t.Errorf("Expected an err but got no err") |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
const solcVersion = "0.9.23" |
|
||||||
|
|
||||||
func TestCompileSolidity(t *testing.T) { |
|
||||||
|
|
||||||
solc, err := compiler.New("") |
|
||||||
if solc == nil { |
|
||||||
t.Skip("no solc found: skip") |
|
||||||
} else if solc.Version() != solcVersion { |
|
||||||
t.Skip("WARNING: skipping test because of solc different version (%v, test written for %v, may need to update)", solc.Version(), solcVersion) |
|
||||||
} |
|
||||||
source := `contract test {\n` + |
|
||||||
" /// @notice Will multiply `a` by 7." + `\n` + |
|
||||||
` function multiply(uint a) returns(uint d) {\n` + |
|
||||||
` return a * 7;\n` + |
|
||||||
` }\n` + |
|
||||||
`}\n` |
|
||||||
|
|
||||||
jsonstr := `{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["` + source + `"],"id":64}` |
|
||||||
|
|
||||||
expCode := "0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056" |
|
||||||
expAbiDefinition := `[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]` |
|
||||||
expUserDoc := `{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}}` |
|
||||||
expDeveloperDoc := `{"methods":{}}` |
|
||||||
expCompilerVersion := solc.Version() |
|
||||||
expLanguage := "Solidity" |
|
||||||
expLanguageVersion := "0" |
|
||||||
expSource := source |
|
||||||
|
|
||||||
eth := ð.Ethereum{} |
|
||||||
xeth := xeth.NewTest(nil, nil) |
|
||||||
api := NewEthApi(xeth, eth, codec.JSON) |
|
||||||
|
|
||||||
var rpcRequest shared.Request |
|
||||||
json.Unmarshal([]byte(jsonstr), &rpcRequest) |
|
||||||
|
|
||||||
response, err := api.CompileSolidity(&rpcRequest) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("Execution failed, %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
respjson, err := json.Marshal(response) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("expected no error, got %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
var contracts = make(map[string]*compiler.Contract) |
|
||||||
err = json.Unmarshal(respjson, &contracts) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("expected no error, got %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
if len(contracts) != 1 { |
|
||||||
t.Errorf("expected one contract, got %v", len(contracts)) |
|
||||||
} |
|
||||||
|
|
||||||
contract := contracts["test"] |
|
||||||
|
|
||||||
if contract.Code != expCode { |
|
||||||
t.Errorf("Expected \n%s got \n%s", expCode, contract.Code) |
|
||||||
} |
|
||||||
|
|
||||||
if strconv.Quote(contract.Info.Source) != `"`+expSource+`"` { |
|
||||||
t.Errorf("Expected \n'%s' got \n'%s'", expSource, strconv.Quote(contract.Info.Source)) |
|
||||||
} |
|
||||||
|
|
||||||
if contract.Info.Language != expLanguage { |
|
||||||
t.Errorf("Expected %s got %s", expLanguage, contract.Info.Language) |
|
||||||
} |
|
||||||
|
|
||||||
if contract.Info.LanguageVersion != expLanguageVersion { |
|
||||||
t.Errorf("Expected %s got %s", expLanguageVersion, contract.Info.LanguageVersion) |
|
||||||
} |
|
||||||
|
|
||||||
if contract.Info.CompilerVersion != expCompilerVersion { |
|
||||||
t.Errorf("Expected %s got %s", expCompilerVersion, contract.Info.CompilerVersion) |
|
||||||
} |
|
||||||
|
|
||||||
userdoc, err := json.Marshal(contract.Info.UserDoc) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("expected no error, got %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
devdoc, err := json.Marshal(contract.Info.DeveloperDoc) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("expected no error, got %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
abidef, err := json.Marshal(contract.Info.AbiDefinition) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("expected no error, got %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
if string(abidef) != expAbiDefinition { |
|
||||||
t.Errorf("Expected \n'%s' got \n'%s'", expAbiDefinition, string(abidef)) |
|
||||||
} |
|
||||||
|
|
||||||
if string(userdoc) != expUserDoc { |
|
||||||
t.Errorf("Expected \n'%s' got \n'%s'", expUserDoc, string(userdoc)) |
|
||||||
} |
|
||||||
|
|
||||||
if string(devdoc) != expDeveloperDoc { |
|
||||||
t.Errorf("Expected %s got %s", expDeveloperDoc, string(devdoc)) |
|
||||||
} |
|
||||||
} |
|
@ -1,74 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
type CompileArgs struct { |
|
||||||
Source string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 1 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1) |
|
||||||
} |
|
||||||
argstr, ok := obj[0].(string) |
|
||||||
if !ok { |
|
||||||
return shared.NewInvalidTypeError("arg0", "is not a string") |
|
||||||
} |
|
||||||
args.Source = argstr |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type FilterStringArgs struct { |
|
||||||
Word string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 1 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1) |
|
||||||
} |
|
||||||
|
|
||||||
var argstr string |
|
||||||
argstr, ok := obj[0].(string) |
|
||||||
if !ok { |
|
||||||
return shared.NewInvalidTypeError("filter", "not a string") |
|
||||||
} |
|
||||||
switch argstr { |
|
||||||
case "latest", "pending": |
|
||||||
break |
|
||||||
default: |
|
||||||
return shared.NewValidationError("Word", "Must be `latest` or `pending`") |
|
||||||
} |
|
||||||
args.Word = argstr |
|
||||||
return nil |
|
||||||
} |
|
File diff suppressed because it is too large
Load Diff
@ -1,144 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/ethereum/go-ethereum/eth" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
"github.com/ethereum/go-ethereum/xeth" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
DbApiversion = "1.0" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// mapping between methods and handlers
|
|
||||||
DbMapping = map[string]dbhandler{ |
|
||||||
"db_getString": (*dbApi).GetString, |
|
||||||
"db_putString": (*dbApi).PutString, |
|
||||||
"db_getHex": (*dbApi).GetHex, |
|
||||||
"db_putHex": (*dbApi).PutHex, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
// db callback handler
|
|
||||||
type dbhandler func(*dbApi, *shared.Request) (interface{}, error) |
|
||||||
|
|
||||||
// db api provider
|
|
||||||
type dbApi struct { |
|
||||||
xeth *xeth.XEth |
|
||||||
ethereum *eth.Ethereum |
|
||||||
methods map[string]dbhandler |
|
||||||
codec codec.ApiCoder |
|
||||||
} |
|
||||||
|
|
||||||
// create a new db api instance
|
|
||||||
func NewDbApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *dbApi { |
|
||||||
return &dbApi{ |
|
||||||
xeth: xeth, |
|
||||||
ethereum: ethereum, |
|
||||||
methods: DbMapping, |
|
||||||
codec: coder.New(nil), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// collection with supported methods
|
|
||||||
func (self *dbApi) Methods() []string { |
|
||||||
methods := make([]string, len(self.methods)) |
|
||||||
i := 0 |
|
||||||
for k := range self.methods { |
|
||||||
methods[i] = k |
|
||||||
i++ |
|
||||||
} |
|
||||||
return methods |
|
||||||
} |
|
||||||
|
|
||||||
// Execute given request
|
|
||||||
func (self *dbApi) Execute(req *shared.Request) (interface{}, error) { |
|
||||||
if callback, ok := self.methods[req.Method]; ok { |
|
||||||
return callback(self, req) |
|
||||||
} |
|
||||||
|
|
||||||
return nil, &shared.NotImplementedError{req.Method} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *dbApi) Name() string { |
|
||||||
return shared.DbApiName |
|
||||||
} |
|
||||||
|
|
||||||
func (self *dbApi) ApiVersion() string { |
|
||||||
return DbApiversion |
|
||||||
} |
|
||||||
|
|
||||||
func (self *dbApi) GetString(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(DbArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if err := args.requirements(); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
ret, err := self.xeth.DbGet([]byte(args.Database + args.Key)) |
|
||||||
return string(ret), err |
|
||||||
} |
|
||||||
|
|
||||||
func (self *dbApi) PutString(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(DbArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if err := args.requirements(); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return self.xeth.DbPut([]byte(args.Database+args.Key), args.Value), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *dbApi) GetHex(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(DbHexArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if err := args.requirements(); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
if res, err := self.xeth.DbGet([]byte(args.Database + args.Key)); err == nil { |
|
||||||
return newHexData(res), nil |
|
||||||
} else { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *dbApi) PutHex(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(DbHexArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if err := args.requirements(); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return self.xeth.DbPut([]byte(args.Database+args.Key), args.Value), nil |
|
||||||
} |
|
@ -1,126 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
type DbArgs struct { |
|
||||||
Database string |
|
||||||
Key string |
|
||||||
Value []byte |
|
||||||
} |
|
||||||
|
|
||||||
func (args *DbArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 2 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 2) |
|
||||||
} |
|
||||||
|
|
||||||
var objstr string |
|
||||||
var ok bool |
|
||||||
|
|
||||||
if objstr, ok = obj[0].(string); !ok { |
|
||||||
return shared.NewInvalidTypeError("database", "not a string") |
|
||||||
} |
|
||||||
args.Database = objstr |
|
||||||
|
|
||||||
if objstr, ok = obj[1].(string); !ok { |
|
||||||
return shared.NewInvalidTypeError("key", "not a string") |
|
||||||
} |
|
||||||
args.Key = objstr |
|
||||||
|
|
||||||
if len(obj) > 2 { |
|
||||||
objstr, ok = obj[2].(string) |
|
||||||
if !ok { |
|
||||||
return shared.NewInvalidTypeError("value", "not a string") |
|
||||||
} |
|
||||||
|
|
||||||
args.Value = []byte(objstr) |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (a *DbArgs) requirements() error { |
|
||||||
if len(a.Database) == 0 { |
|
||||||
return shared.NewValidationError("Database", "cannot be blank") |
|
||||||
} |
|
||||||
if len(a.Key) == 0 { |
|
||||||
return shared.NewValidationError("Key", "cannot be blank") |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type DbHexArgs struct { |
|
||||||
Database string |
|
||||||
Key string |
|
||||||
Value []byte |
|
||||||
} |
|
||||||
|
|
||||||
func (args *DbHexArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 2 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 2) |
|
||||||
} |
|
||||||
|
|
||||||
var objstr string |
|
||||||
var ok bool |
|
||||||
|
|
||||||
if objstr, ok = obj[0].(string); !ok { |
|
||||||
return shared.NewInvalidTypeError("database", "not a string") |
|
||||||
} |
|
||||||
args.Database = objstr |
|
||||||
|
|
||||||
if objstr, ok = obj[1].(string); !ok { |
|
||||||
return shared.NewInvalidTypeError("key", "not a string") |
|
||||||
} |
|
||||||
args.Key = objstr |
|
||||||
|
|
||||||
if len(obj) > 2 { |
|
||||||
objstr, ok = obj[2].(string) |
|
||||||
if !ok { |
|
||||||
return shared.NewInvalidTypeError("value", "not a string") |
|
||||||
} |
|
||||||
|
|
||||||
args.Value = common.FromHex(objstr) |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (a *DbHexArgs) requirements() error { |
|
||||||
if len(a.Database) == 0 { |
|
||||||
return shared.NewValidationError("Database", "cannot be blank") |
|
||||||
} |
|
||||||
if len(a.Key) == 0 { |
|
||||||
return shared.NewValidationError("Key", "cannot be blank") |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
@ -1,29 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
const Db_JS = ` |
|
||||||
web3._extend({ |
|
||||||
property: 'db', |
|
||||||
methods: |
|
||||||
[ |
|
||||||
], |
|
||||||
properties: |
|
||||||
[ |
|
||||||
] |
|
||||||
}); |
|
||||||
` |
|
@ -1,303 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"strings" |
|
||||||
"time" |
|
||||||
|
|
||||||
"github.com/ethereum/ethash" |
|
||||||
"github.com/ethereum/go-ethereum/core" |
|
||||||
"github.com/ethereum/go-ethereum/core/state" |
|
||||||
"github.com/ethereum/go-ethereum/core/vm" |
|
||||||
"github.com/ethereum/go-ethereum/eth" |
|
||||||
"github.com/ethereum/go-ethereum/rlp" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
"github.com/ethereum/go-ethereum/xeth" |
|
||||||
"github.com/rcrowley/go-metrics" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
DebugApiVersion = "1.0" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// mapping between methods and handlers
|
|
||||||
DebugMapping = map[string]debughandler{ |
|
||||||
"debug_dumpBlock": (*debugApi).DumpBlock, |
|
||||||
"debug_getBlockRlp": (*debugApi).GetBlockRlp, |
|
||||||
"debug_printBlock": (*debugApi).PrintBlock, |
|
||||||
"debug_processBlock": (*debugApi).ProcessBlock, |
|
||||||
"debug_seedHash": (*debugApi).SeedHash, |
|
||||||
"debug_setHead": (*debugApi).SetHead, |
|
||||||
"debug_metrics": (*debugApi).Metrics, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
// debug callback handler
|
|
||||||
type debughandler func(*debugApi, *shared.Request) (interface{}, error) |
|
||||||
|
|
||||||
// admin api provider
|
|
||||||
type debugApi struct { |
|
||||||
xeth *xeth.XEth |
|
||||||
ethereum *eth.Ethereum |
|
||||||
methods map[string]debughandler |
|
||||||
codec codec.ApiCoder |
|
||||||
} |
|
||||||
|
|
||||||
// create a new debug api instance
|
|
||||||
func NewDebugApi(xeth *xeth.XEth, ethereum *eth.Ethereum, coder codec.Codec) *debugApi { |
|
||||||
return &debugApi{ |
|
||||||
xeth: xeth, |
|
||||||
ethereum: ethereum, |
|
||||||
methods: DebugMapping, |
|
||||||
codec: coder.New(nil), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// collection with supported methods
|
|
||||||
func (self *debugApi) Methods() []string { |
|
||||||
methods := make([]string, len(self.methods)) |
|
||||||
i := 0 |
|
||||||
for k := range self.methods { |
|
||||||
methods[i] = k |
|
||||||
i++ |
|
||||||
} |
|
||||||
return methods |
|
||||||
} |
|
||||||
|
|
||||||
// Execute given request
|
|
||||||
func (self *debugApi) Execute(req *shared.Request) (interface{}, error) { |
|
||||||
if callback, ok := self.methods[req.Method]; ok { |
|
||||||
return callback(self, req) |
|
||||||
} |
|
||||||
|
|
||||||
return nil, &shared.NotImplementedError{req.Method} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *debugApi) Name() string { |
|
||||||
return shared.DebugApiName |
|
||||||
} |
|
||||||
|
|
||||||
func (self *debugApi) ApiVersion() string { |
|
||||||
return DebugApiVersion |
|
||||||
} |
|
||||||
|
|
||||||
func (self *debugApi) PrintBlock(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(BlockNumArg) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
block := self.xeth.EthBlockByNumber(args.BlockNumber) |
|
||||||
return fmt.Sprintf("%s", block), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *debugApi) DumpBlock(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(BlockNumArg) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
block := self.xeth.EthBlockByNumber(args.BlockNumber) |
|
||||||
if block == nil { |
|
||||||
return nil, fmt.Errorf("block #%d not found", args.BlockNumber) |
|
||||||
} |
|
||||||
|
|
||||||
stateDb, err := state.New(block.Root(), self.ethereum.ChainDb()) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return stateDb.RawDump(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *debugApi) GetBlockRlp(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(BlockNumArg) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
block := self.xeth.EthBlockByNumber(args.BlockNumber) |
|
||||||
if block == nil { |
|
||||||
return nil, fmt.Errorf("block #%d not found", args.BlockNumber) |
|
||||||
} |
|
||||||
encoded, err := rlp.EncodeToBytes(block) |
|
||||||
return fmt.Sprintf("%x", encoded), err |
|
||||||
} |
|
||||||
|
|
||||||
func (self *debugApi) SetHead(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(BlockNumArg) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
self.ethereum.BlockChain().SetHead(uint64(args.BlockNumber)) |
|
||||||
|
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *debugApi) ProcessBlock(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(BlockNumArg) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
block := self.xeth.EthBlockByNumber(args.BlockNumber) |
|
||||||
if block == nil { |
|
||||||
return nil, fmt.Errorf("block #%d not found", args.BlockNumber) |
|
||||||
} |
|
||||||
|
|
||||||
old := vm.Debug |
|
||||||
defer func() { vm.Debug = old }() |
|
||||||
vm.Debug = true |
|
||||||
|
|
||||||
var ( |
|
||||||
blockchain = self.ethereum.BlockChain() |
|
||||||
validator = blockchain.Validator() |
|
||||||
processor = blockchain.Processor() |
|
||||||
) |
|
||||||
|
|
||||||
err := core.ValidateHeader(blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), self.ethereum.ChainDb()) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
receipts, _, usedGas, err := processor.Process(block, statedb) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
err = validator.ValidateState(block, blockchain.GetBlock(block.ParentHash()), statedb, receipts, usedGas) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *debugApi) SeedHash(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(BlockNumArg) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if hash, err := ethash.GetSeedHash(uint64(args.BlockNumber)); err == nil { |
|
||||||
return fmt.Sprintf("0x%x", hash), nil |
|
||||||
} else { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *debugApi) Metrics(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(MetricsArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
// Create a rate formatter
|
|
||||||
units := []string{"", "K", "M", "G", "T", "E", "P"} |
|
||||||
round := func(value float64, prec int) string { |
|
||||||
unit := 0 |
|
||||||
for value >= 1000 { |
|
||||||
unit, value, prec = unit+1, value/1000, 2 |
|
||||||
} |
|
||||||
return fmt.Sprintf(fmt.Sprintf("%%.%df%s", prec, units[unit]), value) |
|
||||||
} |
|
||||||
format := func(total float64, rate float64) string { |
|
||||||
return fmt.Sprintf("%s (%s/s)", round(total, 0), round(rate, 2)) |
|
||||||
} |
|
||||||
// Iterate over all the metrics, and just dump for now
|
|
||||||
counters := make(map[string]interface{}) |
|
||||||
metrics.DefaultRegistry.Each(func(name string, metric interface{}) { |
|
||||||
// Create or retrieve the counter hierarchy for this metric
|
|
||||||
root, parts := counters, strings.Split(name, "/") |
|
||||||
for _, part := range parts[:len(parts)-1] { |
|
||||||
if _, ok := root[part]; !ok { |
|
||||||
root[part] = make(map[string]interface{}) |
|
||||||
} |
|
||||||
root = root[part].(map[string]interface{}) |
|
||||||
} |
|
||||||
name = parts[len(parts)-1] |
|
||||||
|
|
||||||
// Fill the counter with the metric details, formatting if requested
|
|
||||||
if args.Raw { |
|
||||||
switch metric := metric.(type) { |
|
||||||
case metrics.Meter: |
|
||||||
root[name] = map[string]interface{}{ |
|
||||||
"AvgRate01Min": metric.Rate1(), |
|
||||||
"AvgRate05Min": metric.Rate5(), |
|
||||||
"AvgRate15Min": metric.Rate15(), |
|
||||||
"MeanRate": metric.RateMean(), |
|
||||||
"Overall": float64(metric.Count()), |
|
||||||
} |
|
||||||
|
|
||||||
case metrics.Timer: |
|
||||||
root[name] = map[string]interface{}{ |
|
||||||
"AvgRate01Min": metric.Rate1(), |
|
||||||
"AvgRate05Min": metric.Rate5(), |
|
||||||
"AvgRate15Min": metric.Rate15(), |
|
||||||
"MeanRate": metric.RateMean(), |
|
||||||
"Overall": float64(metric.Count()), |
|
||||||
"Percentiles": map[string]interface{}{ |
|
||||||
"5": metric.Percentile(0.05), |
|
||||||
"20": metric.Percentile(0.2), |
|
||||||
"50": metric.Percentile(0.5), |
|
||||||
"80": metric.Percentile(0.8), |
|
||||||
"95": metric.Percentile(0.95), |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
default: |
|
||||||
root[name] = "Unknown metric type" |
|
||||||
} |
|
||||||
} else { |
|
||||||
switch metric := metric.(type) { |
|
||||||
case metrics.Meter: |
|
||||||
root[name] = map[string]interface{}{ |
|
||||||
"Avg01Min": format(metric.Rate1()*60, metric.Rate1()), |
|
||||||
"Avg05Min": format(metric.Rate5()*300, metric.Rate5()), |
|
||||||
"Avg15Min": format(metric.Rate15()*900, metric.Rate15()), |
|
||||||
"Overall": format(float64(metric.Count()), metric.RateMean()), |
|
||||||
} |
|
||||||
|
|
||||||
case metrics.Timer: |
|
||||||
root[name] = map[string]interface{}{ |
|
||||||
"Avg01Min": format(metric.Rate1()*60, metric.Rate1()), |
|
||||||
"Avg05Min": format(metric.Rate5()*300, metric.Rate5()), |
|
||||||
"Avg15Min": format(metric.Rate15()*900, metric.Rate15()), |
|
||||||
"Overall": format(float64(metric.Count()), metric.RateMean()), |
|
||||||
"Maximum": time.Duration(metric.Max()).String(), |
|
||||||
"Minimum": time.Duration(metric.Min()).String(), |
|
||||||
"Percentiles": map[string]interface{}{ |
|
||||||
"5": time.Duration(metric.Percentile(0.05)).String(), |
|
||||||
"20": time.Duration(metric.Percentile(0.2)).String(), |
|
||||||
"50": time.Duration(metric.Percentile(0.5)).String(), |
|
||||||
"80": time.Duration(metric.Percentile(0.8)).String(), |
|
||||||
"95": time.Duration(metric.Percentile(0.95)).String(), |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
default: |
|
||||||
root[name] = "Unknown metric type" |
|
||||||
} |
|
||||||
} |
|
||||||
}) |
|
||||||
return counters, nil |
|
||||||
} |
|
@ -1,87 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
"fmt" |
|
||||||
"math/big" |
|
||||||
"reflect" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
type WaitForBlockArgs struct { |
|
||||||
MinHeight int |
|
||||||
Timeout int // in seconds
|
|
||||||
} |
|
||||||
|
|
||||||
func (args *WaitForBlockArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) > 2 { |
|
||||||
return fmt.Errorf("waitForArgs needs 0, 1, 2 arguments") |
|
||||||
} |
|
||||||
|
|
||||||
// default values when not provided
|
|
||||||
args.MinHeight = -1 |
|
||||||
args.Timeout = -1 |
|
||||||
|
|
||||||
if len(obj) >= 1 { |
|
||||||
var minHeight *big.Int |
|
||||||
if minHeight, err = numString(obj[0]); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
args.MinHeight = int(minHeight.Int64()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 2 { |
|
||||||
timeout, err := numString(obj[1]) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
args.Timeout = int(timeout.Int64()) |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type MetricsArgs struct { |
|
||||||
Raw bool |
|
||||||
} |
|
||||||
|
|
||||||
func (args *MetricsArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
if len(obj) > 1 { |
|
||||||
return fmt.Errorf("metricsArgs needs 0, 1 arguments") |
|
||||||
} |
|
||||||
// default values when not provided
|
|
||||||
if len(obj) >= 1 && obj[0] != nil { |
|
||||||
if value, ok := obj[0].(bool); !ok { |
|
||||||
return fmt.Errorf("invalid argument %v", reflect.TypeOf(obj[0])) |
|
||||||
} else { |
|
||||||
args.Raw = value |
|
||||||
} |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
@ -1,83 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
const Debug_JS = ` |
|
||||||
web3._extend({ |
|
||||||
property: 'debug', |
|
||||||
methods: |
|
||||||
[ |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'printBlock', |
|
||||||
call: 'debug_printBlock', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'getBlockRlp', |
|
||||||
call: 'debug_getBlockRlp', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'setHead', |
|
||||||
call: 'debug_setHead', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'processBlock', |
|
||||||
call: 'debug_processBlock', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'seedHash', |
|
||||||
call: 'debug_seedHash', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'dumpBlock', |
|
||||||
call: 'debug_dumpBlock', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'metrics', |
|
||||||
call: 'debug_metrics', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'verbosity', |
|
||||||
call: 'debug_verbosity', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [web3._extend.utils.fromDecimal] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'vmodule', |
|
||||||
call: 'debug_vmodule', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
], |
|
||||||
properties: |
|
||||||
[ |
|
||||||
] |
|
||||||
}); |
|
||||||
` |
|
@ -1,721 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"encoding/json" |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"fmt" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/common/natspec" |
|
||||||
"github.com/ethereum/go-ethereum/eth" |
|
||||||
"github.com/ethereum/go-ethereum/rlp" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
"github.com/ethereum/go-ethereum/xeth" |
|
||||||
"gopkg.in/fatih/set.v0" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
EthApiVersion = "1.0" |
|
||||||
) |
|
||||||
|
|
||||||
// eth api provider
|
|
||||||
// See https://github.com/ethereum/wiki/wiki/JSON-RPC
|
|
||||||
type ethApi struct { |
|
||||||
xeth *xeth.XEth |
|
||||||
ethereum *eth.Ethereum |
|
||||||
methods map[string]ethhandler |
|
||||||
codec codec.ApiCoder |
|
||||||
} |
|
||||||
|
|
||||||
// eth callback handler
|
|
||||||
type ethhandler func(*ethApi, *shared.Request) (interface{}, error) |
|
||||||
|
|
||||||
var ( |
|
||||||
ethMapping = map[string]ethhandler{ |
|
||||||
"eth_accounts": (*ethApi).Accounts, |
|
||||||
"eth_blockNumber": (*ethApi).BlockNumber, |
|
||||||
"eth_getBalance": (*ethApi).GetBalance, |
|
||||||
"eth_protocolVersion": (*ethApi).ProtocolVersion, |
|
||||||
"eth_coinbase": (*ethApi).Coinbase, |
|
||||||
"eth_mining": (*ethApi).IsMining, |
|
||||||
"eth_syncing": (*ethApi).IsSyncing, |
|
||||||
"eth_gasPrice": (*ethApi).GasPrice, |
|
||||||
"eth_getStorage": (*ethApi).GetStorage, |
|
||||||
"eth_storageAt": (*ethApi).GetStorage, |
|
||||||
"eth_getStorageAt": (*ethApi).GetStorageAt, |
|
||||||
"eth_getTransactionCount": (*ethApi).GetTransactionCount, |
|
||||||
"eth_getBlockTransactionCountByHash": (*ethApi).GetBlockTransactionCountByHash, |
|
||||||
"eth_getBlockTransactionCountByNumber": (*ethApi).GetBlockTransactionCountByNumber, |
|
||||||
"eth_getUncleCountByBlockHash": (*ethApi).GetUncleCountByBlockHash, |
|
||||||
"eth_getUncleCountByBlockNumber": (*ethApi).GetUncleCountByBlockNumber, |
|
||||||
"eth_getData": (*ethApi).GetData, |
|
||||||
"eth_getCode": (*ethApi).GetData, |
|
||||||
"eth_getNatSpec": (*ethApi).GetNatSpec, |
|
||||||
"eth_sign": (*ethApi).Sign, |
|
||||||
"eth_sendRawTransaction": (*ethApi).SubmitTransaction, |
|
||||||
"eth_submitTransaction": (*ethApi).SubmitTransaction, |
|
||||||
"eth_sendTransaction": (*ethApi).SendTransaction, |
|
||||||
"eth_signTransaction": (*ethApi).SignTransaction, |
|
||||||
"eth_transact": (*ethApi).SendTransaction, |
|
||||||
"eth_estimateGas": (*ethApi).EstimateGas, |
|
||||||
"eth_call": (*ethApi).Call, |
|
||||||
"eth_flush": (*ethApi).Flush, |
|
||||||
"eth_getBlockByHash": (*ethApi).GetBlockByHash, |
|
||||||
"eth_getBlockByNumber": (*ethApi).GetBlockByNumber, |
|
||||||
"eth_getTransactionByHash": (*ethApi).GetTransactionByHash, |
|
||||||
"eth_getTransactionByBlockNumberAndIndex": (*ethApi).GetTransactionByBlockNumberAndIndex, |
|
||||||
"eth_getTransactionByBlockHashAndIndex": (*ethApi).GetTransactionByBlockHashAndIndex, |
|
||||||
"eth_getUncleByBlockHashAndIndex": (*ethApi).GetUncleByBlockHashAndIndex, |
|
||||||
"eth_getUncleByBlockNumberAndIndex": (*ethApi).GetUncleByBlockNumberAndIndex, |
|
||||||
"eth_getCompilers": (*ethApi).GetCompilers, |
|
||||||
"eth_compileSolidity": (*ethApi).CompileSolidity, |
|
||||||
"eth_newFilter": (*ethApi).NewFilter, |
|
||||||
"eth_newBlockFilter": (*ethApi).NewBlockFilter, |
|
||||||
"eth_newPendingTransactionFilter": (*ethApi).NewPendingTransactionFilter, |
|
||||||
"eth_uninstallFilter": (*ethApi).UninstallFilter, |
|
||||||
"eth_getFilterChanges": (*ethApi).GetFilterChanges, |
|
||||||
"eth_getFilterLogs": (*ethApi).GetFilterLogs, |
|
||||||
"eth_getLogs": (*ethApi).GetLogs, |
|
||||||
"eth_hashrate": (*ethApi).Hashrate, |
|
||||||
"eth_getWork": (*ethApi).GetWork, |
|
||||||
"eth_submitWork": (*ethApi).SubmitWork, |
|
||||||
"eth_submitHashrate": (*ethApi).SubmitHashrate, |
|
||||||
"eth_resend": (*ethApi).Resend, |
|
||||||
"eth_pendingTransactions": (*ethApi).PendingTransactions, |
|
||||||
"eth_getTransactionReceipt": (*ethApi).GetTransactionReceipt, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
// create new ethApi instance
|
|
||||||
func NewEthApi(xeth *xeth.XEth, eth *eth.Ethereum, codec codec.Codec) *ethApi { |
|
||||||
return ðApi{xeth, eth, ethMapping, codec.New(nil)} |
|
||||||
} |
|
||||||
|
|
||||||
// collection with supported methods
|
|
||||||
func (self *ethApi) Methods() []string { |
|
||||||
methods := make([]string, len(self.methods)) |
|
||||||
i := 0 |
|
||||||
for k := range self.methods { |
|
||||||
methods[i] = k |
|
||||||
i++ |
|
||||||
} |
|
||||||
return methods |
|
||||||
} |
|
||||||
|
|
||||||
// Execute given request
|
|
||||||
func (self *ethApi) Execute(req *shared.Request) (interface{}, error) { |
|
||||||
if callback, ok := self.methods[req.Method]; ok { |
|
||||||
return callback(self, req) |
|
||||||
} |
|
||||||
|
|
||||||
return nil, shared.NewNotImplementedError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) Name() string { |
|
||||||
return shared.EthApiName |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) ApiVersion() string { |
|
||||||
return EthApiVersion |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) Accounts(req *shared.Request) (interface{}, error) { |
|
||||||
return self.xeth.Accounts(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) Hashrate(req *shared.Request) (interface{}, error) { |
|
||||||
return newHexNum(self.xeth.HashRate()), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) BlockNumber(req *shared.Request) (interface{}, error) { |
|
||||||
num := self.xeth.CurrentBlock().Number() |
|
||||||
return newHexNum(num.Bytes()), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetBalance(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(GetBalanceArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
return self.xeth.AtStateNum(args.BlockNumber).BalanceAt(args.Address), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) ProtocolVersion(req *shared.Request) (interface{}, error) { |
|
||||||
return self.xeth.EthVersion(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) Coinbase(req *shared.Request) (interface{}, error) { |
|
||||||
return newHexData(self.xeth.Coinbase()), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) IsMining(req *shared.Request) (interface{}, error) { |
|
||||||
return self.xeth.IsMining(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) IsSyncing(req *shared.Request) (interface{}, error) { |
|
||||||
origin, current, height := self.ethereum.Downloader().Progress() |
|
||||||
if current < height { |
|
||||||
return map[string]interface{}{ |
|
||||||
"startingBlock": newHexNum(big.NewInt(int64(origin)).Bytes()), |
|
||||||
"currentBlock": newHexNum(big.NewInt(int64(current)).Bytes()), |
|
||||||
"highestBlock": newHexNum(big.NewInt(int64(height)).Bytes()), |
|
||||||
}, nil |
|
||||||
} |
|
||||||
return false, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GasPrice(req *shared.Request) (interface{}, error) { |
|
||||||
return newHexNum(self.xeth.DefaultGasPrice().Bytes()), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetStorage(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(GetStorageArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
return self.xeth.AtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetStorageAt(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(GetStorageAtArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
return self.xeth.AtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetTransactionCount(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(GetTxCountArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
count := self.xeth.AtStateNum(args.BlockNumber).TxCountAt(args.Address) |
|
||||||
return fmt.Sprintf("%#x", count), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetBlockTransactionCountByHash(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(HashArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
block := self.xeth.EthBlockByHash(args.Hash) |
|
||||||
if block == nil { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
return fmt.Sprintf("%#x", len(block.Transactions())), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetBlockTransactionCountByNumber(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(BlockNumArg) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
block := self.xeth.EthBlockByNumber(args.BlockNumber) |
|
||||||
if block == nil { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
return fmt.Sprintf("%#x", len(block.Transactions())), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetUncleCountByBlockHash(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(HashArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
block := self.xeth.EthBlockByHash(args.Hash) |
|
||||||
if block == nil { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
return fmt.Sprintf("%#x", len(block.Uncles())), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetUncleCountByBlockNumber(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(BlockNumArg) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
block := self.xeth.EthBlockByNumber(args.BlockNumber) |
|
||||||
if block == nil { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
return fmt.Sprintf("%#x", len(block.Uncles())), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetData(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(GetDataArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
v := self.xeth.AtStateNum(args.BlockNumber).CodeAtBytes(args.Address) |
|
||||||
return newHexData(v), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) Sign(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(NewSigArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
v, err := self.xeth.Sign(args.From, args.Data, false) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return v, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) SubmitTransaction(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(NewDataArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
v, err := self.xeth.PushTx(args.Data) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return v, nil |
|
||||||
} |
|
||||||
|
|
||||||
// JsonTransaction is returned as response by the JSON RPC. It contains the
|
|
||||||
// signed RLP encoded transaction as Raw and the signed transaction object as Tx.
|
|
||||||
type JsonTransaction struct { |
|
||||||
Raw string `json:"raw"` |
|
||||||
Tx *tx `json:"tx"` |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) SignTransaction(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(NewTxArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
// nonce may be nil ("guess" mode)
|
|
||||||
var nonce string |
|
||||||
if args.Nonce != nil { |
|
||||||
nonce = args.Nonce.String() |
|
||||||
} |
|
||||||
|
|
||||||
var gas, price string |
|
||||||
if args.Gas != nil { |
|
||||||
gas = args.Gas.String() |
|
||||||
} |
|
||||||
if args.GasPrice != nil { |
|
||||||
price = args.GasPrice.String() |
|
||||||
} |
|
||||||
tx, err := self.xeth.SignTransaction(args.From, args.To, nonce, args.Value.String(), gas, price, args.Data) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
data, err := rlp.EncodeToBytes(tx) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return JsonTransaction{"0x" + common.Bytes2Hex(data), newTx(tx)}, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) SendTransaction(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(NewTxArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
// nonce may be nil ("guess" mode)
|
|
||||||
var nonce string |
|
||||||
if args.Nonce != nil { |
|
||||||
nonce = args.Nonce.String() |
|
||||||
} |
|
||||||
|
|
||||||
var gas, price string |
|
||||||
if args.Gas != nil { |
|
||||||
gas = args.Gas.String() |
|
||||||
} |
|
||||||
if args.GasPrice != nil { |
|
||||||
price = args.GasPrice.String() |
|
||||||
} |
|
||||||
v, err := self.xeth.Transact(args.From, args.To, nonce, args.Value.String(), gas, price, args.Data) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return v, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetNatSpec(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(NewTxArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
var jsontx = fmt.Sprintf(`{"params":[{"to":"%s","data": "%s"}]}`, args.To, args.Data) |
|
||||||
notice := natspec.GetNotice(self.xeth, jsontx, self.ethereum.HTTPClient()) |
|
||||||
|
|
||||||
return notice, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) { |
|
||||||
_, gas, err := self.doCall(req.Params) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
// TODO unwrap the parent method's ToHex call
|
|
||||||
if len(gas) == 0 { |
|
||||||
return newHexNum(0), nil |
|
||||||
} else { |
|
||||||
return newHexNum(common.String2Big(gas)), err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) Call(req *shared.Request) (interface{}, error) { |
|
||||||
v, _, err := self.doCall(req.Params) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
// TODO unwrap the parent method's ToHex call
|
|
||||||
if v == "0x0" { |
|
||||||
return newHexData([]byte{}), nil |
|
||||||
} else { |
|
||||||
return newHexData(common.FromHex(v)), nil |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) Flush(req *shared.Request) (interface{}, error) { |
|
||||||
return nil, shared.NewNotImplementedError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) doCall(params json.RawMessage) (string, string, error) { |
|
||||||
args := new(CallArgs) |
|
||||||
if err := self.codec.Decode(params, &args); err != nil { |
|
||||||
return "", "", err |
|
||||||
} |
|
||||||
|
|
||||||
return self.xeth.AtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetBlockByHash(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(GetBlockByHashArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
block := self.xeth.EthBlockByHash(args.BlockHash) |
|
||||||
if block == nil { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
return NewBlockRes(block, self.xeth.Td(block.Hash()), args.IncludeTxs), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetBlockByNumber(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(GetBlockByNumberArgs) |
|
||||||
if err := json.Unmarshal(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
block := self.xeth.EthBlockByNumber(args.BlockNumber) |
|
||||||
if block == nil { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
return NewBlockRes(block, self.xeth.Td(block.Hash()), args.IncludeTxs), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetTransactionByHash(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(HashArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash) |
|
||||||
if tx != nil { |
|
||||||
v := NewTransactionRes(tx) |
|
||||||
// if the blockhash is 0, assume this is a pending transaction
|
|
||||||
if bytes.Compare(bhash.Bytes(), bytes.Repeat([]byte{0}, 32)) != 0 { |
|
||||||
v.BlockHash = newHexData(bhash) |
|
||||||
v.BlockNumber = newHexNum(bnum) |
|
||||||
v.TxIndex = newHexNum(txi) |
|
||||||
} |
|
||||||
return v, nil |
|
||||||
} |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetTransactionByBlockHashAndIndex(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(HashIndexArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
raw := self.xeth.EthBlockByHash(args.Hash) |
|
||||||
if raw == nil { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), true) |
|
||||||
if args.Index >= int64(len(block.Transactions)) || args.Index < 0 { |
|
||||||
return nil, nil |
|
||||||
} else { |
|
||||||
return block.Transactions[args.Index], nil |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetTransactionByBlockNumberAndIndex(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(BlockNumIndexArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
raw := self.xeth.EthBlockByNumber(args.BlockNumber) |
|
||||||
if raw == nil { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), true) |
|
||||||
if args.Index >= int64(len(block.Transactions)) || args.Index < 0 { |
|
||||||
// return NewValidationError("Index", "does not exist")
|
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
return block.Transactions[args.Index], nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetUncleByBlockHashAndIndex(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(HashIndexArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
raw := self.xeth.EthBlockByHash(args.Hash) |
|
||||||
if raw == nil { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), false) |
|
||||||
if args.Index >= int64(len(block.Uncles)) || args.Index < 0 { |
|
||||||
// return NewValidationError("Index", "does not exist")
|
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
return block.Uncles[args.Index], nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetUncleByBlockNumberAndIndex(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(BlockNumIndexArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
raw := self.xeth.EthBlockByNumber(args.BlockNumber) |
|
||||||
if raw == nil { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), true) |
|
||||||
if args.Index >= int64(len(block.Uncles)) || args.Index < 0 { |
|
||||||
return nil, nil |
|
||||||
} else { |
|
||||||
return block.Uncles[args.Index], nil |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetCompilers(req *shared.Request) (interface{}, error) { |
|
||||||
var lang string |
|
||||||
if solc, _ := self.xeth.Solc(); solc != nil { |
|
||||||
lang = "Solidity" |
|
||||||
} |
|
||||||
c := []string{lang} |
|
||||||
return c, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) CompileSolidity(req *shared.Request) (interface{}, error) { |
|
||||||
solc, _ := self.xeth.Solc() |
|
||||||
if solc == nil { |
|
||||||
return nil, shared.NewNotAvailableError(req.Method, "solc (solidity compiler) not found") |
|
||||||
} |
|
||||||
|
|
||||||
args := new(SourceArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
contracts, err := solc.Compile(args.Source) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return contracts, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) NewFilter(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(BlockFilterArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
id := self.xeth.NewLogFilter(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics) |
|
||||||
return newHexNum(big.NewInt(int64(id)).Bytes()), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) NewBlockFilter(req *shared.Request) (interface{}, error) { |
|
||||||
return newHexNum(self.xeth.NewBlockFilter()), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) NewPendingTransactionFilter(req *shared.Request) (interface{}, error) { |
|
||||||
return newHexNum(self.xeth.NewTransactionFilter()), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) UninstallFilter(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(FilterIdArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
return self.xeth.UninstallFilter(args.Id), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetFilterChanges(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(FilterIdArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
switch self.xeth.GetFilterType(args.Id) { |
|
||||||
case xeth.BlockFilterTy: |
|
||||||
return NewHashesRes(self.xeth.BlockFilterChanged(args.Id)), nil |
|
||||||
case xeth.TransactionFilterTy: |
|
||||||
return NewHashesRes(self.xeth.TransactionFilterChanged(args.Id)), nil |
|
||||||
case xeth.LogFilterTy: |
|
||||||
return NewLogsRes(self.xeth.LogFilterChanged(args.Id)), nil |
|
||||||
default: |
|
||||||
return []string{}, nil // reply empty string slice
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetFilterLogs(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(FilterIdArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
return NewLogsRes(self.xeth.Logs(args.Id)), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetLogs(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(BlockFilterArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
return NewLogsRes(self.xeth.AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetWork(req *shared.Request) (interface{}, error) { |
|
||||||
self.xeth.SetMining(true, 0) |
|
||||||
ret, err := self.xeth.RemoteMining().GetWork() |
|
||||||
if err != nil { |
|
||||||
return nil, shared.NewNotReadyError("mining work") |
|
||||||
} else { |
|
||||||
return ret, nil |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) SubmitWork(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(SubmitWorkArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
return self.xeth.RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header)), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) SubmitHashrate(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(SubmitHashRateArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return false, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
self.xeth.RemoteMining().SubmitHashrate(common.HexToHash(args.Id), args.Rate) |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) Resend(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(ResendArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
from := common.HexToAddress(args.Tx.From) |
|
||||||
|
|
||||||
pending := self.ethereum.TxPool().GetTransactions() |
|
||||||
for _, p := range pending { |
|
||||||
if pFrom, err := p.From(); err == nil && pFrom == from && p.SigHash() == args.Tx.tx.SigHash() { |
|
||||||
self.ethereum.TxPool().RemoveTx(common.HexToHash(args.Tx.Hash)) |
|
||||||
return self.xeth.Transact(args.Tx.From, args.Tx.To, args.Tx.Nonce, args.Tx.Value, args.GasLimit, args.GasPrice, args.Tx.Data) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil, fmt.Errorf("Transaction %s not found", args.Tx.Hash) |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) PendingTransactions(req *shared.Request) (interface{}, error) { |
|
||||||
txs := self.ethereum.TxPool().GetTransactions() |
|
||||||
|
|
||||||
// grab the accounts from the account manager. This will help with determining which
|
|
||||||
// transactions should be returned.
|
|
||||||
accounts, err := self.ethereum.AccountManager().Accounts() |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
// Add the accouns to a new set
|
|
||||||
accountSet := set.New() |
|
||||||
for _, account := range accounts { |
|
||||||
accountSet.Add(account.Address) |
|
||||||
} |
|
||||||
|
|
||||||
var ltxs []*tx |
|
||||||
for _, tx := range txs { |
|
||||||
if from, _ := tx.From(); accountSet.Has(from) { |
|
||||||
ltxs = append(ltxs, newTx(tx)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return ltxs, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ethApi) GetTransactionReceipt(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(HashArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
txhash := common.BytesToHash(common.FromHex(args.Hash)) |
|
||||||
tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash) |
|
||||||
rec := self.xeth.GetTxReceipt(txhash) |
|
||||||
// We could have an error of "not found". Should disambiguate
|
|
||||||
// if err != nil {
|
|
||||||
// return err, nil
|
|
||||||
// }
|
|
||||||
if rec != nil && tx != nil { |
|
||||||
v := NewReceiptRes(rec) |
|
||||||
v.BlockHash = newHexData(bhash) |
|
||||||
v.BlockNumber = newHexNum(bnum) |
|
||||||
v.TransactionIndex = newHexNum(txi) |
|
||||||
return v, nil |
|
||||||
} |
|
||||||
|
|
||||||
return nil, nil |
|
||||||
} |
|
File diff suppressed because it is too large
Load Diff
@ -1,66 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
// JS api provided by web3.js
|
|
||||||
// eth_sign not standard
|
|
||||||
|
|
||||||
const Eth_JS = ` |
|
||||||
web3._extend({ |
|
||||||
property: 'eth', |
|
||||||
methods: |
|
||||||
[ |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'sign', |
|
||||||
call: 'eth_sign', |
|
||||||
params: 2, |
|
||||||
inputFormatter: [web3._extend.utils.toAddress, null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'resend', |
|
||||||
call: 'eth_resend', |
|
||||||
params: 3, |
|
||||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, web3._extend.utils.fromDecimal, web3._extend.utils.fromDecimal] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'getNatSpec', |
|
||||||
call: 'eth_getNatSpec', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'signTransaction', |
|
||||||
call: 'eth_signTransaction', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'submitTransaction', |
|
||||||
call: 'eth_submitTransaction', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter] |
|
||||||
}) |
|
||||||
], |
|
||||||
properties: |
|
||||||
[ |
|
||||||
new web3._extend.Property({ |
|
||||||
name: 'pendingTransactions', |
|
||||||
getter: 'eth_pendingTransactions' |
|
||||||
}) |
|
||||||
] |
|
||||||
}); |
|
||||||
` |
|
@ -1,88 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/ethereum/go-ethereum/logger" |
|
||||||
"github.com/ethereum/go-ethereum/logger/glog" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
MergedApiVersion = "1.0" |
|
||||||
) |
|
||||||
|
|
||||||
// combines multiple API's
|
|
||||||
type MergedApi struct { |
|
||||||
apis map[string]string |
|
||||||
methods map[string]shared.EthereumApi |
|
||||||
} |
|
||||||
|
|
||||||
// create new merged api instance
|
|
||||||
func newMergedApi(apis ...shared.EthereumApi) *MergedApi { |
|
||||||
mergedApi := new(MergedApi) |
|
||||||
mergedApi.apis = make(map[string]string, len(apis)) |
|
||||||
mergedApi.methods = make(map[string]shared.EthereumApi) |
|
||||||
|
|
||||||
for _, api := range apis { |
|
||||||
if api != nil { |
|
||||||
mergedApi.apis[api.Name()] = api.ApiVersion() |
|
||||||
for _, method := range api.Methods() { |
|
||||||
mergedApi.methods[method] = api |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return mergedApi |
|
||||||
} |
|
||||||
|
|
||||||
// Supported RPC methods
|
|
||||||
func (self *MergedApi) Methods() []string { |
|
||||||
all := make([]string, len(self.methods)) |
|
||||||
for method, _ := range self.methods { |
|
||||||
all = append(all, method) |
|
||||||
} |
|
||||||
return all |
|
||||||
} |
|
||||||
|
|
||||||
// Call the correct API's Execute method for the given request
|
|
||||||
func (self *MergedApi) Execute(req *shared.Request) (interface{}, error) { |
|
||||||
glog.V(logger.Detail).Infof("%s %s", req.Method, req.Params) |
|
||||||
|
|
||||||
if res, _ := self.handle(req); res != nil { |
|
||||||
return res, nil |
|
||||||
} |
|
||||||
if api, found := self.methods[req.Method]; found { |
|
||||||
return api.Execute(req) |
|
||||||
} |
|
||||||
return nil, shared.NewNotImplementedError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
func (self *MergedApi) Name() string { |
|
||||||
return shared.MergedApiName |
|
||||||
} |
|
||||||
|
|
||||||
func (self *MergedApi) ApiVersion() string { |
|
||||||
return MergedApiVersion |
|
||||||
} |
|
||||||
|
|
||||||
func (self *MergedApi) handle(req *shared.Request) (interface{}, error) { |
|
||||||
if req.Method == "modules" { // provided API's
|
|
||||||
return self.apis, nil |
|
||||||
} |
|
||||||
|
|
||||||
return nil, nil |
|
||||||
} |
|
@ -1,177 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/ethereum/ethash" |
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/eth" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
MinerApiVersion = "1.0" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// mapping between methods and handlers
|
|
||||||
MinerMapping = map[string]minerhandler{ |
|
||||||
"miner_hashrate": (*minerApi).Hashrate, |
|
||||||
"miner_makeDAG": (*minerApi).MakeDAG, |
|
||||||
"miner_setExtra": (*minerApi).SetExtra, |
|
||||||
"miner_setGasPrice": (*minerApi).SetGasPrice, |
|
||||||
"miner_setEtherbase": (*minerApi).SetEtherbase, |
|
||||||
"miner_startAutoDAG": (*minerApi).StartAutoDAG, |
|
||||||
"miner_start": (*minerApi).StartMiner, |
|
||||||
"miner_stopAutoDAG": (*minerApi).StopAutoDAG, |
|
||||||
"miner_stop": (*minerApi).StopMiner, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
// miner callback handler
|
|
||||||
type minerhandler func(*minerApi, *shared.Request) (interface{}, error) |
|
||||||
|
|
||||||
// miner api provider
|
|
||||||
type minerApi struct { |
|
||||||
ethereum *eth.Ethereum |
|
||||||
methods map[string]minerhandler |
|
||||||
codec codec.ApiCoder |
|
||||||
} |
|
||||||
|
|
||||||
// create a new miner api instance
|
|
||||||
func NewMinerApi(ethereum *eth.Ethereum, coder codec.Codec) *minerApi { |
|
||||||
return &minerApi{ |
|
||||||
ethereum: ethereum, |
|
||||||
methods: MinerMapping, |
|
||||||
codec: coder.New(nil), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Execute given request
|
|
||||||
func (self *minerApi) Execute(req *shared.Request) (interface{}, error) { |
|
||||||
if callback, ok := self.methods[req.Method]; ok { |
|
||||||
return callback(self, req) |
|
||||||
} |
|
||||||
|
|
||||||
return nil, &shared.NotImplementedError{req.Method} |
|
||||||
} |
|
||||||
|
|
||||||
// collection with supported methods
|
|
||||||
func (self *minerApi) Methods() []string { |
|
||||||
methods := make([]string, len(self.methods)) |
|
||||||
i := 0 |
|
||||||
for k := range self.methods { |
|
||||||
methods[i] = k |
|
||||||
i++ |
|
||||||
} |
|
||||||
return methods |
|
||||||
} |
|
||||||
|
|
||||||
func (self *minerApi) Name() string { |
|
||||||
return shared.MinerApiName |
|
||||||
} |
|
||||||
|
|
||||||
func (self *minerApi) ApiVersion() string { |
|
||||||
return MinerApiVersion |
|
||||||
} |
|
||||||
|
|
||||||
func (self *minerApi) StartMiner(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(StartMinerArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
if args.Threads == -1 { // (not specified by user, use default)
|
|
||||||
args.Threads = self.ethereum.MinerThreads |
|
||||||
} |
|
||||||
|
|
||||||
self.ethereum.StartAutoDAG() |
|
||||||
err := self.ethereum.StartMining(args.Threads, "") |
|
||||||
if err == nil { |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
func (self *minerApi) StopMiner(req *shared.Request) (interface{}, error) { |
|
||||||
self.ethereum.StopMining() |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *minerApi) Hashrate(req *shared.Request) (interface{}, error) { |
|
||||||
return self.ethereum.Miner().HashRate(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *minerApi) SetExtra(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(SetExtraArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
if err := self.ethereum.Miner().SetExtra([]byte(args.Data)); err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *minerApi) SetGasPrice(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(GasPriceArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
self.ethereum.Miner().SetGasPrice(common.String2Big(args.Price)) |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *minerApi) SetEtherbase(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(SetEtherbaseArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
self.ethereum.SetEtherbase(args.Etherbase) |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *minerApi) StartAutoDAG(req *shared.Request) (interface{}, error) { |
|
||||||
self.ethereum.StartAutoDAG() |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *minerApi) StopAutoDAG(req *shared.Request) (interface{}, error) { |
|
||||||
self.ethereum.StopAutoDAG() |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *minerApi) MakeDAG(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(MakeDAGArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
if args.BlockNumber < 0 { |
|
||||||
return false, shared.NewValidationError("BlockNumber", "BlockNumber must be positive") |
|
||||||
} |
|
||||||
|
|
||||||
err := ethash.MakeDAG(uint64(args.BlockNumber), "") |
|
||||||
if err == nil { |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
return false, err |
|
||||||
} |
|
@ -1,142 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
|
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
type StartMinerArgs struct { |
|
||||||
Threads int |
|
||||||
} |
|
||||||
|
|
||||||
func (args *StartMinerArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) == 0 || obj[0] == nil { |
|
||||||
args.Threads = -1 |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
var num *big.Int |
|
||||||
if num, err = numString(obj[0]); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
args.Threads = int(num.Int64()) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type SetExtraArgs struct { |
|
||||||
Data string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *SetExtraArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 1 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1) |
|
||||||
} |
|
||||||
|
|
||||||
extrastr, ok := obj[0].(string) |
|
||||||
if !ok { |
|
||||||
return shared.NewInvalidTypeError("Price", "not a string") |
|
||||||
} |
|
||||||
args.Data = extrastr |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type GasPriceArgs struct { |
|
||||||
Price string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *GasPriceArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 1 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1) |
|
||||||
} |
|
||||||
|
|
||||||
if pricestr, ok := obj[0].(string); ok { |
|
||||||
args.Price = pricestr |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
return shared.NewInvalidTypeError("Price", "not a string") |
|
||||||
} |
|
||||||
|
|
||||||
type SetEtherbaseArgs struct { |
|
||||||
Etherbase common.Address |
|
||||||
} |
|
||||||
|
|
||||||
func (args *SetEtherbaseArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 1 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1) |
|
||||||
} |
|
||||||
|
|
||||||
if addr, ok := obj[0].(string); ok { |
|
||||||
args.Etherbase = common.HexToAddress(addr) |
|
||||||
if (args.Etherbase == common.Address{}) { |
|
||||||
return shared.NewInvalidTypeError("Etherbase", "not a valid address") |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
return shared.NewInvalidTypeError("Etherbase", "not a string") |
|
||||||
} |
|
||||||
|
|
||||||
type MakeDAGArgs struct { |
|
||||||
BlockNumber int64 |
|
||||||
} |
|
||||||
|
|
||||||
func (args *MakeDAGArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
args.BlockNumber = -1 |
|
||||||
var obj []interface{} |
|
||||||
|
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 1 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1) |
|
||||||
} |
|
||||||
|
|
||||||
if err := blockHeight(obj[0], &args.BlockNumber); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
@ -1,83 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
const Miner_JS = ` |
|
||||||
web3._extend({ |
|
||||||
property: 'miner', |
|
||||||
methods: |
|
||||||
[ |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'start', |
|
||||||
call: 'miner_start', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'stop', |
|
||||||
call: 'miner_stop', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'setEtherbase', |
|
||||||
call: 'miner_setEtherbase', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [web3._extend.formatters.formatInputInt], |
|
||||||
outputFormatter: web3._extend.formatters.formatOutputBool |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'setExtra', |
|
||||||
call: 'miner_setExtra', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'setGasPrice', |
|
||||||
call: 'miner_setGasPrice', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [web3._extend.utils.fromDecial] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'startAutoDAG', |
|
||||||
call: 'miner_startAutoDAG', |
|
||||||
params: 0, |
|
||||||
inputFormatter: [] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'stopAutoDAG', |
|
||||||
call: 'miner_stopAutoDAG', |
|
||||||
params: 0, |
|
||||||
inputFormatter: [] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'makeDAG', |
|
||||||
call: 'miner_makeDAG', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter] |
|
||||||
}) |
|
||||||
], |
|
||||||
properties: |
|
||||||
[ |
|
||||||
new web3._extend.Property({ |
|
||||||
name: 'hashrate', |
|
||||||
getter: 'miner_hashrate', |
|
||||||
outputFormatter: web3._extend.utils.toDecimal |
|
||||||
}) |
|
||||||
] |
|
||||||
}); |
|
||||||
` |
|
@ -1,99 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/ethereum/go-ethereum/eth" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
"github.com/ethereum/go-ethereum/xeth" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
NetApiVersion = "1.0" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// mapping between methods and handlers
|
|
||||||
netMapping = map[string]nethandler{ |
|
||||||
"net_peerCount": (*netApi).PeerCount, |
|
||||||
"net_listening": (*netApi).IsListening, |
|
||||||
"net_version": (*netApi).Version, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
// net callback handler
|
|
||||||
type nethandler func(*netApi, *shared.Request) (interface{}, error) |
|
||||||
|
|
||||||
// net api provider
|
|
||||||
type netApi struct { |
|
||||||
xeth *xeth.XEth |
|
||||||
ethereum *eth.Ethereum |
|
||||||
methods map[string]nethandler |
|
||||||
codec codec.ApiCoder |
|
||||||
} |
|
||||||
|
|
||||||
// create a new net api instance
|
|
||||||
func NewNetApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *netApi { |
|
||||||
return &netApi{ |
|
||||||
xeth: xeth, |
|
||||||
ethereum: eth, |
|
||||||
methods: netMapping, |
|
||||||
codec: coder.New(nil), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// collection with supported methods
|
|
||||||
func (self *netApi) Methods() []string { |
|
||||||
methods := make([]string, len(self.methods)) |
|
||||||
i := 0 |
|
||||||
for k := range self.methods { |
|
||||||
methods[i] = k |
|
||||||
i++ |
|
||||||
} |
|
||||||
return methods |
|
||||||
} |
|
||||||
|
|
||||||
// Execute given request
|
|
||||||
func (self *netApi) Execute(req *shared.Request) (interface{}, error) { |
|
||||||
if callback, ok := self.methods[req.Method]; ok { |
|
||||||
return callback(self, req) |
|
||||||
} |
|
||||||
|
|
||||||
return nil, shared.NewNotImplementedError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
func (self *netApi) Name() string { |
|
||||||
return shared.NetApiName |
|
||||||
} |
|
||||||
|
|
||||||
func (self *netApi) ApiVersion() string { |
|
||||||
return NetApiVersion |
|
||||||
} |
|
||||||
|
|
||||||
// Number of connected peers
|
|
||||||
func (self *netApi) PeerCount(req *shared.Request) (interface{}, error) { |
|
||||||
return newHexNum(self.xeth.PeerCount()), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *netApi) IsListening(req *shared.Request) (interface{}, error) { |
|
||||||
return self.xeth.IsListening(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *netApi) Version(req *shared.Request) (interface{}, error) { |
|
||||||
return self.xeth.NetworkVersion(), nil |
|
||||||
} |
|
@ -1,39 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
const Net_JS = ` |
|
||||||
web3._extend({ |
|
||||||
property: 'net', |
|
||||||
methods: |
|
||||||
[ |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'addPeer', |
|
||||||
call: 'net_addPeer', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null] |
|
||||||
}) |
|
||||||
], |
|
||||||
properties: |
|
||||||
[ |
|
||||||
new web3._extend.Property({ |
|
||||||
name: 'version', |
|
||||||
getter: 'net_version' |
|
||||||
}) |
|
||||||
] |
|
||||||
}); |
|
||||||
` |
|
@ -1,522 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"encoding/binary" |
|
||||||
"encoding/hex" |
|
||||||
"encoding/json" |
|
||||||
"math/big" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
type hexdata struct { |
|
||||||
data []byte |
|
||||||
isNil bool |
|
||||||
} |
|
||||||
|
|
||||||
func (d *hexdata) String() string { |
|
||||||
return "0x" + common.Bytes2Hex(d.data) |
|
||||||
} |
|
||||||
|
|
||||||
func (d *hexdata) MarshalJSON() ([]byte, error) { |
|
||||||
if d.isNil { |
|
||||||
return json.Marshal(nil) |
|
||||||
} |
|
||||||
return json.Marshal(d.String()) |
|
||||||
} |
|
||||||
|
|
||||||
func newHexData(input interface{}) *hexdata { |
|
||||||
d := new(hexdata) |
|
||||||
|
|
||||||
if input == nil { |
|
||||||
d.isNil = true |
|
||||||
return d |
|
||||||
} |
|
||||||
switch input := input.(type) { |
|
||||||
case []byte: |
|
||||||
d.data = input |
|
||||||
case common.Hash: |
|
||||||
d.data = input.Bytes() |
|
||||||
case *common.Hash: |
|
||||||
if input == nil { |
|
||||||
d.isNil = true |
|
||||||
} else { |
|
||||||
d.data = input.Bytes() |
|
||||||
} |
|
||||||
case common.Address: |
|
||||||
d.data = input.Bytes() |
|
||||||
case *common.Address: |
|
||||||
if input == nil { |
|
||||||
d.isNil = true |
|
||||||
} else { |
|
||||||
d.data = input.Bytes() |
|
||||||
} |
|
||||||
case types.Bloom: |
|
||||||
d.data = input.Bytes() |
|
||||||
case *types.Bloom: |
|
||||||
if input == nil { |
|
||||||
d.isNil = true |
|
||||||
} else { |
|
||||||
d.data = input.Bytes() |
|
||||||
} |
|
||||||
case *big.Int: |
|
||||||
if input == nil { |
|
||||||
d.isNil = true |
|
||||||
} else { |
|
||||||
d.data = input.Bytes() |
|
||||||
} |
|
||||||
case int64: |
|
||||||
d.data = big.NewInt(input).Bytes() |
|
||||||
case uint64: |
|
||||||
buff := make([]byte, 8) |
|
||||||
binary.BigEndian.PutUint64(buff, input) |
|
||||||
d.data = buff |
|
||||||
case int: |
|
||||||
d.data = big.NewInt(int64(input)).Bytes() |
|
||||||
case uint: |
|
||||||
d.data = big.NewInt(int64(input)).Bytes() |
|
||||||
case int8: |
|
||||||
d.data = big.NewInt(int64(input)).Bytes() |
|
||||||
case uint8: |
|
||||||
d.data = big.NewInt(int64(input)).Bytes() |
|
||||||
case int16: |
|
||||||
d.data = big.NewInt(int64(input)).Bytes() |
|
||||||
case uint16: |
|
||||||
buff := make([]byte, 2) |
|
||||||
binary.BigEndian.PutUint16(buff, input) |
|
||||||
d.data = buff |
|
||||||
case int32: |
|
||||||
d.data = big.NewInt(int64(input)).Bytes() |
|
||||||
case uint32: |
|
||||||
buff := make([]byte, 4) |
|
||||||
binary.BigEndian.PutUint32(buff, input) |
|
||||||
d.data = buff |
|
||||||
case string: // hexstring
|
|
||||||
// aaargh ffs TODO: avoid back-and-forth hex encodings where unneeded
|
|
||||||
bytes, err := hex.DecodeString(strings.TrimPrefix(input, "0x")) |
|
||||||
if err != nil { |
|
||||||
d.isNil = true |
|
||||||
} else { |
|
||||||
d.data = bytes |
|
||||||
} |
|
||||||
default: |
|
||||||
d.isNil = true |
|
||||||
} |
|
||||||
|
|
||||||
return d |
|
||||||
} |
|
||||||
|
|
||||||
type hexnum struct { |
|
||||||
data []byte |
|
||||||
isNil bool |
|
||||||
} |
|
||||||
|
|
||||||
func (d *hexnum) String() string { |
|
||||||
// Get hex string from bytes
|
|
||||||
out := common.Bytes2Hex(d.data) |
|
||||||
// Trim leading 0s
|
|
||||||
out = strings.TrimLeft(out, "0") |
|
||||||
// Output "0x0" when value is 0
|
|
||||||
if len(out) == 0 { |
|
||||||
out = "0" |
|
||||||
} |
|
||||||
return "0x" + out |
|
||||||
} |
|
||||||
|
|
||||||
func (d *hexnum) MarshalJSON() ([]byte, error) { |
|
||||||
if d.isNil { |
|
||||||
return json.Marshal(nil) |
|
||||||
} |
|
||||||
return json.Marshal(d.String()) |
|
||||||
} |
|
||||||
|
|
||||||
func newHexNum(input interface{}) *hexnum { |
|
||||||
d := new(hexnum) |
|
||||||
|
|
||||||
d.data = newHexData(input).data |
|
||||||
|
|
||||||
return d |
|
||||||
} |
|
||||||
|
|
||||||
type BlockRes struct { |
|
||||||
fullTx bool |
|
||||||
|
|
||||||
BlockNumber *hexnum `json:"number"` |
|
||||||
BlockHash *hexdata `json:"hash"` |
|
||||||
ParentHash *hexdata `json:"parentHash"` |
|
||||||
Nonce *hexdata `json:"nonce"` |
|
||||||
Sha3Uncles *hexdata `json:"sha3Uncles"` |
|
||||||
LogsBloom *hexdata `json:"logsBloom"` |
|
||||||
TransactionRoot *hexdata `json:"transactionsRoot"` |
|
||||||
StateRoot *hexdata `json:"stateRoot"` |
|
||||||
ReceiptRoot *hexdata `json:"receiptRoot"` |
|
||||||
Miner *hexdata `json:"miner"` |
|
||||||
Difficulty *hexnum `json:"difficulty"` |
|
||||||
TotalDifficulty *hexnum `json:"totalDifficulty"` |
|
||||||
Size *hexnum `json:"size"` |
|
||||||
ExtraData *hexdata `json:"extraData"` |
|
||||||
GasLimit *hexnum `json:"gasLimit"` |
|
||||||
GasUsed *hexnum `json:"gasUsed"` |
|
||||||
UnixTimestamp *hexnum `json:"timestamp"` |
|
||||||
Transactions []*TransactionRes `json:"transactions"` |
|
||||||
Uncles []*UncleRes `json:"uncles"` |
|
||||||
} |
|
||||||
|
|
||||||
func (b *BlockRes) MarshalJSON() ([]byte, error) { |
|
||||||
if b.fullTx { |
|
||||||
var ext struct { |
|
||||||
BlockNumber *hexnum `json:"number"` |
|
||||||
BlockHash *hexdata `json:"hash"` |
|
||||||
ParentHash *hexdata `json:"parentHash"` |
|
||||||
Nonce *hexdata `json:"nonce"` |
|
||||||
Sha3Uncles *hexdata `json:"sha3Uncles"` |
|
||||||
LogsBloom *hexdata `json:"logsBloom"` |
|
||||||
TransactionRoot *hexdata `json:"transactionsRoot"` |
|
||||||
StateRoot *hexdata `json:"stateRoot"` |
|
||||||
ReceiptRoot *hexdata `json:"receiptRoot"` |
|
||||||
Miner *hexdata `json:"miner"` |
|
||||||
Difficulty *hexnum `json:"difficulty"` |
|
||||||
TotalDifficulty *hexnum `json:"totalDifficulty"` |
|
||||||
Size *hexnum `json:"size"` |
|
||||||
ExtraData *hexdata `json:"extraData"` |
|
||||||
GasLimit *hexnum `json:"gasLimit"` |
|
||||||
GasUsed *hexnum `json:"gasUsed"` |
|
||||||
UnixTimestamp *hexnum `json:"timestamp"` |
|
||||||
Transactions []*TransactionRes `json:"transactions"` |
|
||||||
Uncles []*hexdata `json:"uncles"` |
|
||||||
} |
|
||||||
|
|
||||||
ext.BlockNumber = b.BlockNumber |
|
||||||
ext.BlockHash = b.BlockHash |
|
||||||
ext.ParentHash = b.ParentHash |
|
||||||
ext.Nonce = b.Nonce |
|
||||||
ext.Sha3Uncles = b.Sha3Uncles |
|
||||||
ext.LogsBloom = b.LogsBloom |
|
||||||
ext.TransactionRoot = b.TransactionRoot |
|
||||||
ext.StateRoot = b.StateRoot |
|
||||||
ext.ReceiptRoot = b.ReceiptRoot |
|
||||||
ext.Miner = b.Miner |
|
||||||
ext.Difficulty = b.Difficulty |
|
||||||
ext.TotalDifficulty = b.TotalDifficulty |
|
||||||
ext.Size = b.Size |
|
||||||
ext.ExtraData = b.ExtraData |
|
||||||
ext.GasLimit = b.GasLimit |
|
||||||
ext.GasUsed = b.GasUsed |
|
||||||
ext.UnixTimestamp = b.UnixTimestamp |
|
||||||
ext.Transactions = b.Transactions |
|
||||||
ext.Uncles = make([]*hexdata, len(b.Uncles)) |
|
||||||
for i, u := range b.Uncles { |
|
||||||
ext.Uncles[i] = u.BlockHash |
|
||||||
} |
|
||||||
return json.Marshal(ext) |
|
||||||
} else { |
|
||||||
var ext struct { |
|
||||||
BlockNumber *hexnum `json:"number"` |
|
||||||
BlockHash *hexdata `json:"hash"` |
|
||||||
ParentHash *hexdata `json:"parentHash"` |
|
||||||
Nonce *hexdata `json:"nonce"` |
|
||||||
Sha3Uncles *hexdata `json:"sha3Uncles"` |
|
||||||
LogsBloom *hexdata `json:"logsBloom"` |
|
||||||
TransactionRoot *hexdata `json:"transactionsRoot"` |
|
||||||
StateRoot *hexdata `json:"stateRoot"` |
|
||||||
ReceiptRoot *hexdata `json:"receiptRoot"` |
|
||||||
Miner *hexdata `json:"miner"` |
|
||||||
Difficulty *hexnum `json:"difficulty"` |
|
||||||
TotalDifficulty *hexnum `json:"totalDifficulty"` |
|
||||||
Size *hexnum `json:"size"` |
|
||||||
ExtraData *hexdata `json:"extraData"` |
|
||||||
GasLimit *hexnum `json:"gasLimit"` |
|
||||||
GasUsed *hexnum `json:"gasUsed"` |
|
||||||
UnixTimestamp *hexnum `json:"timestamp"` |
|
||||||
Transactions []*hexdata `json:"transactions"` |
|
||||||
Uncles []*hexdata `json:"uncles"` |
|
||||||
} |
|
||||||
|
|
||||||
ext.BlockNumber = b.BlockNumber |
|
||||||
ext.BlockHash = b.BlockHash |
|
||||||
ext.ParentHash = b.ParentHash |
|
||||||
ext.Nonce = b.Nonce |
|
||||||
ext.Sha3Uncles = b.Sha3Uncles |
|
||||||
ext.LogsBloom = b.LogsBloom |
|
||||||
ext.TransactionRoot = b.TransactionRoot |
|
||||||
ext.StateRoot = b.StateRoot |
|
||||||
ext.ReceiptRoot = b.ReceiptRoot |
|
||||||
ext.Miner = b.Miner |
|
||||||
ext.Difficulty = b.Difficulty |
|
||||||
ext.TotalDifficulty = b.TotalDifficulty |
|
||||||
ext.Size = b.Size |
|
||||||
ext.ExtraData = b.ExtraData |
|
||||||
ext.GasLimit = b.GasLimit |
|
||||||
ext.GasUsed = b.GasUsed |
|
||||||
ext.UnixTimestamp = b.UnixTimestamp |
|
||||||
ext.Transactions = make([]*hexdata, len(b.Transactions)) |
|
||||||
for i, tx := range b.Transactions { |
|
||||||
ext.Transactions[i] = tx.Hash |
|
||||||
} |
|
||||||
ext.Uncles = make([]*hexdata, len(b.Uncles)) |
|
||||||
for i, u := range b.Uncles { |
|
||||||
ext.Uncles[i] = u.BlockHash |
|
||||||
} |
|
||||||
return json.Marshal(ext) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func NewBlockRes(block *types.Block, td *big.Int, fullTx bool) *BlockRes { |
|
||||||
if block == nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
res := new(BlockRes) |
|
||||||
res.fullTx = fullTx |
|
||||||
res.BlockNumber = newHexNum(block.Number()) |
|
||||||
res.BlockHash = newHexData(block.Hash()) |
|
||||||
res.ParentHash = newHexData(block.ParentHash()) |
|
||||||
res.Nonce = newHexData(block.Nonce()) |
|
||||||
res.Sha3Uncles = newHexData(block.UncleHash()) |
|
||||||
res.LogsBloom = newHexData(block.Bloom()) |
|
||||||
res.TransactionRoot = newHexData(block.TxHash()) |
|
||||||
res.StateRoot = newHexData(block.Root()) |
|
||||||
res.ReceiptRoot = newHexData(block.ReceiptHash()) |
|
||||||
res.Miner = newHexData(block.Coinbase()) |
|
||||||
res.Difficulty = newHexNum(block.Difficulty()) |
|
||||||
res.TotalDifficulty = newHexNum(td) |
|
||||||
res.Size = newHexNum(block.Size().Int64()) |
|
||||||
res.ExtraData = newHexData(block.Extra()) |
|
||||||
res.GasLimit = newHexNum(block.GasLimit()) |
|
||||||
res.GasUsed = newHexNum(block.GasUsed()) |
|
||||||
res.UnixTimestamp = newHexNum(block.Time()) |
|
||||||
|
|
||||||
txs := block.Transactions() |
|
||||||
res.Transactions = make([]*TransactionRes, len(txs)) |
|
||||||
for i, tx := range txs { |
|
||||||
res.Transactions[i] = NewTransactionRes(tx) |
|
||||||
res.Transactions[i].BlockHash = res.BlockHash |
|
||||||
res.Transactions[i].BlockNumber = res.BlockNumber |
|
||||||
res.Transactions[i].TxIndex = newHexNum(i) |
|
||||||
} |
|
||||||
|
|
||||||
uncles := block.Uncles() |
|
||||||
res.Uncles = make([]*UncleRes, len(uncles)) |
|
||||||
for i, uncle := range uncles { |
|
||||||
res.Uncles[i] = NewUncleRes(uncle) |
|
||||||
} |
|
||||||
|
|
||||||
return res |
|
||||||
} |
|
||||||
|
|
||||||
type TransactionRes struct { |
|
||||||
Hash *hexdata `json:"hash"` |
|
||||||
Nonce *hexnum `json:"nonce"` |
|
||||||
BlockHash *hexdata `json:"blockHash"` |
|
||||||
BlockNumber *hexnum `json:"blockNumber"` |
|
||||||
TxIndex *hexnum `json:"transactionIndex"` |
|
||||||
From *hexdata `json:"from"` |
|
||||||
To *hexdata `json:"to"` |
|
||||||
Value *hexnum `json:"value"` |
|
||||||
Gas *hexnum `json:"gas"` |
|
||||||
GasPrice *hexnum `json:"gasPrice"` |
|
||||||
Input *hexdata `json:"input"` |
|
||||||
} |
|
||||||
|
|
||||||
func NewTransactionRes(tx *types.Transaction) *TransactionRes { |
|
||||||
if tx == nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
var v = new(TransactionRes) |
|
||||||
v.Hash = newHexData(tx.Hash()) |
|
||||||
v.Nonce = newHexNum(tx.Nonce()) |
|
||||||
// v.BlockHash =
|
|
||||||
// v.BlockNumber =
|
|
||||||
// v.TxIndex =
|
|
||||||
from, _ := tx.From() |
|
||||||
v.From = newHexData(from) |
|
||||||
v.To = newHexData(tx.To()) |
|
||||||
v.Value = newHexNum(tx.Value()) |
|
||||||
v.Gas = newHexNum(tx.Gas()) |
|
||||||
v.GasPrice = newHexNum(tx.GasPrice()) |
|
||||||
v.Input = newHexData(tx.Data()) |
|
||||||
return v |
|
||||||
} |
|
||||||
|
|
||||||
type UncleRes struct { |
|
||||||
BlockNumber *hexnum `json:"number"` |
|
||||||
BlockHash *hexdata `json:"hash"` |
|
||||||
ParentHash *hexdata `json:"parentHash"` |
|
||||||
Nonce *hexdata `json:"nonce"` |
|
||||||
Sha3Uncles *hexdata `json:"sha3Uncles"` |
|
||||||
ReceiptHash *hexdata `json:"receiptHash"` |
|
||||||
LogsBloom *hexdata `json:"logsBloom"` |
|
||||||
TransactionRoot *hexdata `json:"transactionsRoot"` |
|
||||||
StateRoot *hexdata `json:"stateRoot"` |
|
||||||
Miner *hexdata `json:"miner"` |
|
||||||
Difficulty *hexnum `json:"difficulty"` |
|
||||||
ExtraData *hexdata `json:"extraData"` |
|
||||||
GasLimit *hexnum `json:"gasLimit"` |
|
||||||
GasUsed *hexnum `json:"gasUsed"` |
|
||||||
UnixTimestamp *hexnum `json:"timestamp"` |
|
||||||
} |
|
||||||
|
|
||||||
func NewUncleRes(h *types.Header) *UncleRes { |
|
||||||
if h == nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
var v = new(UncleRes) |
|
||||||
v.BlockNumber = newHexNum(h.Number) |
|
||||||
v.BlockHash = newHexData(h.Hash()) |
|
||||||
v.ParentHash = newHexData(h.ParentHash) |
|
||||||
v.Sha3Uncles = newHexData(h.UncleHash) |
|
||||||
v.Nonce = newHexData(h.Nonce[:]) |
|
||||||
v.LogsBloom = newHexData(h.Bloom) |
|
||||||
v.TransactionRoot = newHexData(h.TxHash) |
|
||||||
v.StateRoot = newHexData(h.Root) |
|
||||||
v.Miner = newHexData(h.Coinbase) |
|
||||||
v.Difficulty = newHexNum(h.Difficulty) |
|
||||||
v.ExtraData = newHexData(h.Extra) |
|
||||||
v.GasLimit = newHexNum(h.GasLimit) |
|
||||||
v.GasUsed = newHexNum(h.GasUsed) |
|
||||||
v.UnixTimestamp = newHexNum(h.Time) |
|
||||||
v.ReceiptHash = newHexData(h.ReceiptHash) |
|
||||||
|
|
||||||
return v |
|
||||||
} |
|
||||||
|
|
||||||
// type FilterLogRes struct {
|
|
||||||
// Hash string `json:"hash"`
|
|
||||||
// Address string `json:"address"`
|
|
||||||
// Data string `json:"data"`
|
|
||||||
// BlockNumber string `json:"blockNumber"`
|
|
||||||
// TransactionHash string `json:"transactionHash"`
|
|
||||||
// BlockHash string `json:"blockHash"`
|
|
||||||
// TransactionIndex string `json:"transactionIndex"`
|
|
||||||
// LogIndex string `json:"logIndex"`
|
|
||||||
// }
|
|
||||||
|
|
||||||
// type FilterWhisperRes struct {
|
|
||||||
// Hash string `json:"hash"`
|
|
||||||
// From string `json:"from"`
|
|
||||||
// To string `json:"to"`
|
|
||||||
// Expiry string `json:"expiry"`
|
|
||||||
// Sent string `json:"sent"`
|
|
||||||
// Ttl string `json:"ttl"`
|
|
||||||
// Topics string `json:"topics"`
|
|
||||||
// Payload string `json:"payload"`
|
|
||||||
// WorkProved string `json:"workProved"`
|
|
||||||
// }
|
|
||||||
|
|
||||||
type ReceiptRes struct { |
|
||||||
TransactionHash *hexdata `json:"transactionHash"` |
|
||||||
TransactionIndex *hexnum `json:"transactionIndex"` |
|
||||||
BlockNumber *hexnum `json:"blockNumber"` |
|
||||||
BlockHash *hexdata `json:"blockHash"` |
|
||||||
CumulativeGasUsed *hexnum `json:"cumulativeGasUsed"` |
|
||||||
GasUsed *hexnum `json:"gasUsed"` |
|
||||||
ContractAddress *hexdata `json:"contractAddress"` |
|
||||||
Logs *[]interface{} `json:"logs"` |
|
||||||
} |
|
||||||
|
|
||||||
func NewReceiptRes(rec *types.Receipt) *ReceiptRes { |
|
||||||
if rec == nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
var v = new(ReceiptRes) |
|
||||||
v.TransactionHash = newHexData(rec.TxHash) |
|
||||||
if rec.GasUsed != nil { |
|
||||||
v.GasUsed = newHexNum(rec.GasUsed.Bytes()) |
|
||||||
} |
|
||||||
v.CumulativeGasUsed = newHexNum(rec.CumulativeGasUsed) |
|
||||||
|
|
||||||
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
|
|
||||||
if bytes.Compare(rec.ContractAddress.Bytes(), bytes.Repeat([]byte{0}, 20)) != 0 { |
|
||||||
v.ContractAddress = newHexData(rec.ContractAddress) |
|
||||||
} |
|
||||||
|
|
||||||
logs := make([]interface{}, len(rec.Logs)) |
|
||||||
for i, log := range rec.Logs { |
|
||||||
logs[i] = NewLogRes(log) |
|
||||||
} |
|
||||||
v.Logs = &logs |
|
||||||
|
|
||||||
return v |
|
||||||
} |
|
||||||
|
|
||||||
func numString(raw interface{}) (*big.Int, error) { |
|
||||||
var number *big.Int |
|
||||||
// Parse as integer
|
|
||||||
num, ok := raw.(float64) |
|
||||||
if ok { |
|
||||||
number = big.NewInt(int64(num)) |
|
||||||
return number, nil |
|
||||||
} |
|
||||||
|
|
||||||
// Parse as string/hexstring
|
|
||||||
str, ok := raw.(string) |
|
||||||
if ok { |
|
||||||
number = common.String2Big(str) |
|
||||||
return number, nil |
|
||||||
} |
|
||||||
|
|
||||||
return nil, shared.NewInvalidTypeError("", "not a number or string") |
|
||||||
} |
|
||||||
|
|
||||||
func blockHeight(raw interface{}, number *int64) error { |
|
||||||
// Parse as integer
|
|
||||||
num, ok := raw.(float64) |
|
||||||
if ok { |
|
||||||
*number = int64(num) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// Parse as string/hexstring
|
|
||||||
str, ok := raw.(string) |
|
||||||
if !ok { |
|
||||||
return shared.NewInvalidTypeError("", "not a number or string") |
|
||||||
} |
|
||||||
|
|
||||||
switch str { |
|
||||||
case "earliest": |
|
||||||
*number = 0 |
|
||||||
case "latest": |
|
||||||
*number = -1 |
|
||||||
case "pending": |
|
||||||
*number = -2 |
|
||||||
default: |
|
||||||
if common.HasHexPrefix(str) { |
|
||||||
*number = common.String2Big(str).Int64() |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("blockNumber", "is not a valid string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func blockHeightFromJson(msg json.RawMessage, number *int64) error { |
|
||||||
var raw interface{} |
|
||||||
if err := json.Unmarshal(msg, &raw); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
return blockHeight(raw, number) |
|
||||||
} |
|
@ -1,139 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"time" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/eth" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
"github.com/ethereum/go-ethereum/xeth" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
PersonalApiVersion = "1.0" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// mapping between methods and handlers
|
|
||||||
personalMapping = map[string]personalhandler{ |
|
||||||
"personal_listAccounts": (*personalApi).ListAccounts, |
|
||||||
"personal_newAccount": (*personalApi).NewAccount, |
|
||||||
"personal_unlockAccount": (*personalApi).UnlockAccount, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
// net callback handler
|
|
||||||
type personalhandler func(*personalApi, *shared.Request) (interface{}, error) |
|
||||||
|
|
||||||
// net api provider
|
|
||||||
type personalApi struct { |
|
||||||
xeth *xeth.XEth |
|
||||||
ethereum *eth.Ethereum |
|
||||||
methods map[string]personalhandler |
|
||||||
codec codec.ApiCoder |
|
||||||
} |
|
||||||
|
|
||||||
// create a new net api instance
|
|
||||||
func NewPersonalApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *personalApi { |
|
||||||
return &personalApi{ |
|
||||||
xeth: xeth, |
|
||||||
ethereum: eth, |
|
||||||
methods: personalMapping, |
|
||||||
codec: coder.New(nil), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// collection with supported methods
|
|
||||||
func (self *personalApi) Methods() []string { |
|
||||||
methods := make([]string, len(self.methods)) |
|
||||||
i := 0 |
|
||||||
for k := range self.methods { |
|
||||||
methods[i] = k |
|
||||||
i++ |
|
||||||
} |
|
||||||
return methods |
|
||||||
} |
|
||||||
|
|
||||||
// Execute given request
|
|
||||||
func (self *personalApi) Execute(req *shared.Request) (interface{}, error) { |
|
||||||
if callback, ok := self.methods[req.Method]; ok { |
|
||||||
return callback(self, req) |
|
||||||
} |
|
||||||
|
|
||||||
return nil, shared.NewNotImplementedError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
func (self *personalApi) Name() string { |
|
||||||
return shared.PersonalApiName |
|
||||||
} |
|
||||||
|
|
||||||
func (self *personalApi) ApiVersion() string { |
|
||||||
return PersonalApiVersion |
|
||||||
} |
|
||||||
|
|
||||||
func (self *personalApi) ListAccounts(req *shared.Request) (interface{}, error) { |
|
||||||
return self.xeth.Accounts(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *personalApi) NewAccount(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(NewAccountArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
var passwd string |
|
||||||
if args.Passphrase == nil { |
|
||||||
fe := self.xeth.Frontend() |
|
||||||
if fe == nil { |
|
||||||
return false, fmt.Errorf("unable to create account: unable to interact with user") |
|
||||||
} |
|
||||||
var ok bool |
|
||||||
passwd, ok = fe.AskPassword() |
|
||||||
if !ok { |
|
||||||
return false, fmt.Errorf("unable to create account: no password given") |
|
||||||
} |
|
||||||
} else { |
|
||||||
passwd = *args.Passphrase |
|
||||||
} |
|
||||||
am := self.ethereum.AccountManager() |
|
||||||
acc, err := am.NewAccount(passwd) |
|
||||||
return acc.Address.Hex(), err |
|
||||||
} |
|
||||||
|
|
||||||
func (self *personalApi) UnlockAccount(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(UnlockAccountArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if args.Passphrase == nil { |
|
||||||
fe := self.xeth.Frontend() |
|
||||||
if fe == nil { |
|
||||||
return false, fmt.Errorf("No password provided") |
|
||||||
} |
|
||||||
return fe.UnlockAccount(common.HexToAddress(args.Address).Bytes()), nil |
|
||||||
} |
|
||||||
|
|
||||||
am := self.ethereum.AccountManager() |
|
||||||
addr := common.HexToAddress(args.Address) |
|
||||||
|
|
||||||
err := am.TimedUnlock(addr, *args.Passphrase, time.Duration(args.Duration)*time.Second) |
|
||||||
return err == nil, err |
|
||||||
} |
|
@ -1,85 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
type NewAccountArgs struct { |
|
||||||
Passphrase *string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *NewAccountArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 1 && obj[0] != nil { |
|
||||||
if passphrasestr, ok := obj[0].(string); ok { |
|
||||||
args.Passphrase = &passphrasestr |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("passphrase", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type UnlockAccountArgs struct { |
|
||||||
Address string |
|
||||||
Passphrase *string |
|
||||||
Duration int |
|
||||||
} |
|
||||||
|
|
||||||
func (args *UnlockAccountArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
args.Duration = 0 |
|
||||||
|
|
||||||
if len(obj) < 1 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1) |
|
||||||
} |
|
||||||
|
|
||||||
if addrstr, ok := obj[0].(string); ok { |
|
||||||
args.Address = addrstr |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("address", "not a string") |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 2 && obj[1] != nil { |
|
||||||
if passphrasestr, ok := obj[1].(string); ok { |
|
||||||
args.Passphrase = &passphrasestr |
|
||||||
} else { |
|
||||||
return shared.NewInvalidTypeError("passphrase", "not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) >= 3 && obj[2] != nil { |
|
||||||
if duration, ok := obj[2].(float64); ok { |
|
||||||
args.Duration = int(duration) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
@ -1,51 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
const Personal_JS = ` |
|
||||||
web3._extend({ |
|
||||||
property: 'personal', |
|
||||||
methods: |
|
||||||
[ |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'newAccount', |
|
||||||
call: 'personal_newAccount', |
|
||||||
params: 1, |
|
||||||
inputFormatter: [null], |
|
||||||
outputFormatter: web3._extend.utils.toAddress |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'unlockAccount', |
|
||||||
call: 'personal_unlockAccount', |
|
||||||
params: 3, |
|
||||||
inputFormatter: [null, null, null] |
|
||||||
}), |
|
||||||
new web3._extend.Method({ |
|
||||||
name: 'lockAccount', |
|
||||||
call: 'personal_lockAccount', |
|
||||||
params: 1 |
|
||||||
}) |
|
||||||
], |
|
||||||
properties: |
|
||||||
[ |
|
||||||
new web3._extend.Property({ |
|
||||||
name: 'listAccounts', |
|
||||||
getter: 'personal_listAccounts' |
|
||||||
}) |
|
||||||
] |
|
||||||
}); |
|
||||||
` |
|
@ -1,196 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/eth" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
"github.com/ethereum/go-ethereum/xeth" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
ShhApiVersion = "1.0" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// mapping between methods and handlers
|
|
||||||
shhMapping = map[string]shhhandler{ |
|
||||||
"shh_version": (*shhApi).Version, |
|
||||||
"shh_post": (*shhApi).Post, |
|
||||||
"shh_hasIdentity": (*shhApi).HasIdentity, |
|
||||||
"shh_newIdentity": (*shhApi).NewIdentity, |
|
||||||
"shh_newFilter": (*shhApi).NewFilter, |
|
||||||
"shh_uninstallFilter": (*shhApi).UninstallFilter, |
|
||||||
"shh_getMessages": (*shhApi).GetMessages, |
|
||||||
"shh_getFilterChanges": (*shhApi).GetFilterChanges, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
func newWhisperOfflineError(method string) error { |
|
||||||
return shared.NewNotAvailableError(method, "whisper offline") |
|
||||||
} |
|
||||||
|
|
||||||
// net callback handler
|
|
||||||
type shhhandler func(*shhApi, *shared.Request) (interface{}, error) |
|
||||||
|
|
||||||
// shh api provider
|
|
||||||
type shhApi struct { |
|
||||||
xeth *xeth.XEth |
|
||||||
ethereum *eth.Ethereum |
|
||||||
methods map[string]shhhandler |
|
||||||
codec codec.ApiCoder |
|
||||||
} |
|
||||||
|
|
||||||
// create a new whisper api instance
|
|
||||||
func NewShhApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *shhApi { |
|
||||||
return &shhApi{ |
|
||||||
xeth: xeth, |
|
||||||
ethereum: eth, |
|
||||||
methods: shhMapping, |
|
||||||
codec: coder.New(nil), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// collection with supported methods
|
|
||||||
func (self *shhApi) Methods() []string { |
|
||||||
methods := make([]string, len(self.methods)) |
|
||||||
i := 0 |
|
||||||
for k := range self.methods { |
|
||||||
methods[i] = k |
|
||||||
i++ |
|
||||||
} |
|
||||||
return methods |
|
||||||
} |
|
||||||
|
|
||||||
// Execute given request
|
|
||||||
func (self *shhApi) Execute(req *shared.Request) (interface{}, error) { |
|
||||||
if callback, ok := self.methods[req.Method]; ok { |
|
||||||
return callback(self, req) |
|
||||||
} |
|
||||||
|
|
||||||
return nil, shared.NewNotImplementedError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
func (self *shhApi) Name() string { |
|
||||||
return shared.ShhApiName |
|
||||||
} |
|
||||||
|
|
||||||
func (self *shhApi) ApiVersion() string { |
|
||||||
return ShhApiVersion |
|
||||||
} |
|
||||||
|
|
||||||
func (self *shhApi) Version(req *shared.Request) (interface{}, error) { |
|
||||||
w := self.xeth.Whisper() |
|
||||||
if w == nil { |
|
||||||
return nil, newWhisperOfflineError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
return w.Version(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *shhApi) Post(req *shared.Request) (interface{}, error) { |
|
||||||
w := self.xeth.Whisper() |
|
||||||
if w == nil { |
|
||||||
return nil, newWhisperOfflineError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
args := new(WhisperMessageArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
err := w.Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *shhApi) HasIdentity(req *shared.Request) (interface{}, error) { |
|
||||||
w := self.xeth.Whisper() |
|
||||||
if w == nil { |
|
||||||
return nil, newWhisperOfflineError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
args := new(WhisperIdentityArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return w.HasIdentity(args.Identity), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *shhApi) NewIdentity(req *shared.Request) (interface{}, error) { |
|
||||||
w := self.xeth.Whisper() |
|
||||||
if w == nil { |
|
||||||
return nil, newWhisperOfflineError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
return w.NewIdentity(), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *shhApi) NewFilter(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(WhisperFilterArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
id := self.xeth.NewWhisperFilter(args.To, args.From, args.Topics) |
|
||||||
return newHexNum(big.NewInt(int64(id)).Bytes()), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *shhApi) UninstallFilter(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(FilterIdArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return self.xeth.UninstallWhisperFilter(args.Id), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *shhApi) GetFilterChanges(req *shared.Request) (interface{}, error) { |
|
||||||
w := self.xeth.Whisper() |
|
||||||
if w == nil { |
|
||||||
return nil, newWhisperOfflineError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
// Retrieve all the new messages arrived since the last request
|
|
||||||
args := new(FilterIdArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return self.xeth.WhisperMessagesChanged(args.Id), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *shhApi) GetMessages(req *shared.Request) (interface{}, error) { |
|
||||||
w := self.xeth.Whisper() |
|
||||||
if w == nil { |
|
||||||
return nil, newWhisperOfflineError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
// Retrieve all the cached messages matching a specific, existing filter
|
|
||||||
args := new(FilterIdArgs) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return self.xeth.WhisperMessages(args.Id), nil |
|
||||||
} |
|
@ -1,174 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
"fmt" |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
type WhisperMessageArgs struct { |
|
||||||
Payload string |
|
||||||
To string |
|
||||||
From string |
|
||||||
Topics []string |
|
||||||
Priority uint32 |
|
||||||
Ttl uint32 |
|
||||||
} |
|
||||||
|
|
||||||
func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []struct { |
|
||||||
Payload string |
|
||||||
To string |
|
||||||
From string |
|
||||||
Topics []string |
|
||||||
Priority interface{} |
|
||||||
Ttl interface{} |
|
||||||
} |
|
||||||
|
|
||||||
if err = json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 1 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1) |
|
||||||
} |
|
||||||
args.Payload = obj[0].Payload |
|
||||||
args.To = obj[0].To |
|
||||||
args.From = obj[0].From |
|
||||||
args.Topics = obj[0].Topics |
|
||||||
|
|
||||||
var num *big.Int |
|
||||||
if num, err = numString(obj[0].Priority); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
args.Priority = uint32(num.Int64()) |
|
||||||
|
|
||||||
if num, err = numString(obj[0].Ttl); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
args.Ttl = uint32(num.Int64()) |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type WhisperIdentityArgs struct { |
|
||||||
Identity string |
|
||||||
} |
|
||||||
|
|
||||||
func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
var obj []interface{} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
|
|
||||||
if len(obj) < 1 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1) |
|
||||||
} |
|
||||||
|
|
||||||
argstr, ok := obj[0].(string) |
|
||||||
if !ok { |
|
||||||
return shared.NewInvalidTypeError("arg0", "not a string") |
|
||||||
} |
|
||||||
|
|
||||||
args.Identity = argstr |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
type WhisperFilterArgs struct { |
|
||||||
To string |
|
||||||
From string |
|
||||||
Topics [][]string |
|
||||||
} |
|
||||||
|
|
||||||
// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a
|
|
||||||
// JSON message blob into a WhisperFilterArgs structure.
|
|
||||||
func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { |
|
||||||
// Unmarshal the JSON message and sanity check
|
|
||||||
var obj []struct { |
|
||||||
To interface{} `json:"to"` |
|
||||||
From interface{} `json:"from"` |
|
||||||
Topics interface{} `json:"topics"` |
|
||||||
} |
|
||||||
if err := json.Unmarshal(b, &obj); err != nil { |
|
||||||
return shared.NewDecodeParamError(err.Error()) |
|
||||||
} |
|
||||||
if len(obj) < 1 { |
|
||||||
return shared.NewInsufficientParamsError(len(obj), 1) |
|
||||||
} |
|
||||||
// Retrieve the simple data contents of the filter arguments
|
|
||||||
if obj[0].To == nil { |
|
||||||
args.To = "" |
|
||||||
} else { |
|
||||||
argstr, ok := obj[0].To.(string) |
|
||||||
if !ok { |
|
||||||
return shared.NewInvalidTypeError("to", "is not a string") |
|
||||||
} |
|
||||||
args.To = argstr |
|
||||||
} |
|
||||||
if obj[0].From == nil { |
|
||||||
args.From = "" |
|
||||||
} else { |
|
||||||
argstr, ok := obj[0].From.(string) |
|
||||||
if !ok { |
|
||||||
return shared.NewInvalidTypeError("from", "is not a string") |
|
||||||
} |
|
||||||
args.From = argstr |
|
||||||
} |
|
||||||
// Construct the nested topic array
|
|
||||||
if obj[0].Topics != nil { |
|
||||||
// Make sure we have an actual topic array
|
|
||||||
list, ok := obj[0].Topics.([]interface{}) |
|
||||||
if !ok { |
|
||||||
return shared.NewInvalidTypeError("topics", "is not an array") |
|
||||||
} |
|
||||||
// Iterate over each topic and handle nil, string or array
|
|
||||||
topics := make([][]string, len(list)) |
|
||||||
for idx, field := range list { |
|
||||||
switch value := field.(type) { |
|
||||||
case nil: |
|
||||||
topics[idx] = []string{} |
|
||||||
|
|
||||||
case string: |
|
||||||
topics[idx] = []string{value} |
|
||||||
|
|
||||||
case []interface{}: |
|
||||||
topics[idx] = make([]string, len(value)) |
|
||||||
for i, nested := range value { |
|
||||||
switch value := nested.(type) { |
|
||||||
case nil: |
|
||||||
topics[idx][i] = "" |
|
||||||
|
|
||||||
case string: |
|
||||||
topics[idx][i] = value |
|
||||||
|
|
||||||
default: |
|
||||||
return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", idx, i), "is not a string") |
|
||||||
} |
|
||||||
} |
|
||||||
default: |
|
||||||
return shared.NewInvalidTypeError(fmt.Sprintf("topic[%d]", idx), "not a string or array") |
|
||||||
} |
|
||||||
} |
|
||||||
args.Topics = topics |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
@ -1,34 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
const Shh_JS = ` |
|
||||||
web3._extend({ |
|
||||||
property: 'shh', |
|
||||||
methods: |
|
||||||
[ |
|
||||||
|
|
||||||
], |
|
||||||
properties: |
|
||||||
[ |
|
||||||
new web3._extend.Property({ |
|
||||||
name: 'version', |
|
||||||
getter: 'shh_version' |
|
||||||
}) |
|
||||||
] |
|
||||||
}); |
|
||||||
` |
|
@ -1,92 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/ethereum/go-ethereum/eth" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
"github.com/ethereum/go-ethereum/xeth" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
TxPoolApiVersion = "1.0" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// mapping between methods and handlers
|
|
||||||
txpoolMapping = map[string]txpoolhandler{ |
|
||||||
"txpool_status": (*txPoolApi).Status, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
// net callback handler
|
|
||||||
type txpoolhandler func(*txPoolApi, *shared.Request) (interface{}, error) |
|
||||||
|
|
||||||
// txpool api provider
|
|
||||||
type txPoolApi struct { |
|
||||||
xeth *xeth.XEth |
|
||||||
ethereum *eth.Ethereum |
|
||||||
methods map[string]txpoolhandler |
|
||||||
codec codec.ApiCoder |
|
||||||
} |
|
||||||
|
|
||||||
// create a new txpool api instance
|
|
||||||
func NewTxPoolApi(xeth *xeth.XEth, eth *eth.Ethereum, coder codec.Codec) *txPoolApi { |
|
||||||
return &txPoolApi{ |
|
||||||
xeth: xeth, |
|
||||||
ethereum: eth, |
|
||||||
methods: txpoolMapping, |
|
||||||
codec: coder.New(nil), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// collection with supported methods
|
|
||||||
func (self *txPoolApi) Methods() []string { |
|
||||||
methods := make([]string, len(self.methods)) |
|
||||||
i := 0 |
|
||||||
for k := range self.methods { |
|
||||||
methods[i] = k |
|
||||||
i++ |
|
||||||
} |
|
||||||
return methods |
|
||||||
} |
|
||||||
|
|
||||||
// Execute given request
|
|
||||||
func (self *txPoolApi) Execute(req *shared.Request) (interface{}, error) { |
|
||||||
if callback, ok := self.methods[req.Method]; ok { |
|
||||||
return callback(self, req) |
|
||||||
} |
|
||||||
|
|
||||||
return nil, shared.NewNotImplementedError(req.Method) |
|
||||||
} |
|
||||||
|
|
||||||
func (self *txPoolApi) Name() string { |
|
||||||
return shared.TxPoolApiName |
|
||||||
} |
|
||||||
|
|
||||||
func (self *txPoolApi) ApiVersion() string { |
|
||||||
return TxPoolApiVersion |
|
||||||
} |
|
||||||
|
|
||||||
func (self *txPoolApi) Status(req *shared.Request) (interface{}, error) { |
|
||||||
pending, queue := self.ethereum.TxPool().Stats() |
|
||||||
return map[string]int{ |
|
||||||
"pending": pending, |
|
||||||
"queued": queue, |
|
||||||
}, nil |
|
||||||
} |
|
@ -1,33 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
const TxPool_JS = ` |
|
||||||
web3._extend({ |
|
||||||
property: 'txpool', |
|
||||||
methods: |
|
||||||
[ |
|
||||||
], |
|
||||||
properties: |
|
||||||
[ |
|
||||||
new web3._extend.Property({ |
|
||||||
name: 'status', |
|
||||||
getter: 'txpool_status' |
|
||||||
}) |
|
||||||
] |
|
||||||
}); |
|
||||||
` |
|
@ -1,226 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"strings" |
|
||||||
|
|
||||||
"fmt" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/eth" |
|
||||||
"github.com/ethereum/go-ethereum/node" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
"github.com/ethereum/go-ethereum/xeth" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// Mapping between the different methods each api supports
|
|
||||||
AutoCompletion = map[string][]string{ |
|
||||||
"admin": []string{ |
|
||||||
"addPeer", |
|
||||||
"datadir", |
|
||||||
"enableUserAgent", |
|
||||||
"exportChain", |
|
||||||
"getContractInfo", |
|
||||||
"httpGet", |
|
||||||
"importChain", |
|
||||||
"nodeInfo", |
|
||||||
"peers", |
|
||||||
"register", |
|
||||||
"registerUrl", |
|
||||||
"saveInfo", |
|
||||||
"setGlobalRegistrar", |
|
||||||
"setHashReg", |
|
||||||
"setUrlHint", |
|
||||||
"setSolc", |
|
||||||
"sleep", |
|
||||||
"sleepBlocks", |
|
||||||
"startNatSpec", |
|
||||||
"startRPC", |
|
||||||
"stopNatSpec", |
|
||||||
"stopRPC", |
|
||||||
"verbosity", |
|
||||||
}, |
|
||||||
"db": []string{ |
|
||||||
"getString", |
|
||||||
"putString", |
|
||||||
"getHex", |
|
||||||
"putHex", |
|
||||||
}, |
|
||||||
"debug": []string{ |
|
||||||
"dumpBlock", |
|
||||||
"getBlockRlp", |
|
||||||
"metrics", |
|
||||||
"printBlock", |
|
||||||
"processBlock", |
|
||||||
"seedHash", |
|
||||||
"setHead", |
|
||||||
}, |
|
||||||
"eth": []string{ |
|
||||||
"accounts", |
|
||||||
"blockNumber", |
|
||||||
"call", |
|
||||||
"contract", |
|
||||||
"coinbase", |
|
||||||
"compile.lll", |
|
||||||
"compile.serpent", |
|
||||||
"compile.solidity", |
|
||||||
"contract", |
|
||||||
"defaultAccount", |
|
||||||
"defaultBlock", |
|
||||||
"estimateGas", |
|
||||||
"filter", |
|
||||||
"getBalance", |
|
||||||
"getBlock", |
|
||||||
"getBlockTransactionCount", |
|
||||||
"getBlockUncleCount", |
|
||||||
"getCode", |
|
||||||
"getNatSpec", |
|
||||||
"getCompilers", |
|
||||||
"gasPrice", |
|
||||||
"getStorageAt", |
|
||||||
"getTransaction", |
|
||||||
"getTransactionCount", |
|
||||||
"getTransactionFromBlock", |
|
||||||
"getTransactionReceipt", |
|
||||||
"getUncle", |
|
||||||
"hashrate", |
|
||||||
"mining", |
|
||||||
"namereg", |
|
||||||
"pendingTransactions", |
|
||||||
"resend", |
|
||||||
"sendRawTransaction", |
|
||||||
"sendTransaction", |
|
||||||
"sign", |
|
||||||
"syncing", |
|
||||||
}, |
|
||||||
"miner": []string{ |
|
||||||
"hashrate", |
|
||||||
"makeDAG", |
|
||||||
"setEtherbase", |
|
||||||
"setExtra", |
|
||||||
"setGasPrice", |
|
||||||
"startAutoDAG", |
|
||||||
"start", |
|
||||||
"stopAutoDAG", |
|
||||||
"stop", |
|
||||||
}, |
|
||||||
"net": []string{ |
|
||||||
"peerCount", |
|
||||||
"listening", |
|
||||||
}, |
|
||||||
"personal": []string{ |
|
||||||
"listAccounts", |
|
||||||
"newAccount", |
|
||||||
"unlockAccount", |
|
||||||
}, |
|
||||||
"shh": []string{ |
|
||||||
"post", |
|
||||||
"newIdentity", |
|
||||||
"hasIdentity", |
|
||||||
"newGroup", |
|
||||||
"addToGroup", |
|
||||||
"filter", |
|
||||||
}, |
|
||||||
"txpool": []string{ |
|
||||||
"status", |
|
||||||
}, |
|
||||||
"web3": []string{ |
|
||||||
"sha3", |
|
||||||
"version", |
|
||||||
"fromWei", |
|
||||||
"toWei", |
|
||||||
"toHex", |
|
||||||
"toAscii", |
|
||||||
"fromAscii", |
|
||||||
"toBigNumber", |
|
||||||
"isAddress", |
|
||||||
}, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
// Parse a comma separated API string to individual api's
|
|
||||||
func ParseApiString(apistr string, codec codec.Codec, xeth *xeth.XEth, stack *node.Node) ([]shared.EthereumApi, error) { |
|
||||||
if len(strings.TrimSpace(apistr)) == 0 { |
|
||||||
return nil, fmt.Errorf("Empty apistr provided") |
|
||||||
} |
|
||||||
|
|
||||||
names := strings.Split(apistr, ",") |
|
||||||
apis := make([]shared.EthereumApi, len(names)) |
|
||||||
|
|
||||||
var eth *eth.Ethereum |
|
||||||
if stack != nil { |
|
||||||
if err := stack.Service(ð); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
} |
|
||||||
for i, name := range names { |
|
||||||
switch strings.ToLower(strings.TrimSpace(name)) { |
|
||||||
case shared.AdminApiName: |
|
||||||
apis[i] = NewAdminApi(xeth, stack, codec) |
|
||||||
case shared.DebugApiName: |
|
||||||
apis[i] = NewDebugApi(xeth, eth, codec) |
|
||||||
case shared.DbApiName: |
|
||||||
apis[i] = NewDbApi(xeth, eth, codec) |
|
||||||
case shared.EthApiName: |
|
||||||
apis[i] = NewEthApi(xeth, eth, codec) |
|
||||||
case shared.MinerApiName: |
|
||||||
apis[i] = NewMinerApi(eth, codec) |
|
||||||
case shared.NetApiName: |
|
||||||
apis[i] = NewNetApi(xeth, eth, codec) |
|
||||||
case shared.ShhApiName: |
|
||||||
apis[i] = NewShhApi(xeth, eth, codec) |
|
||||||
case shared.TxPoolApiName: |
|
||||||
apis[i] = NewTxPoolApi(xeth, eth, codec) |
|
||||||
case shared.PersonalApiName: |
|
||||||
apis[i] = NewPersonalApi(xeth, eth, codec) |
|
||||||
case shared.Web3ApiName: |
|
||||||
apis[i] = NewWeb3Api(xeth, codec) |
|
||||||
case "rpc": // gives information about the RPC interface
|
|
||||||
continue |
|
||||||
default: |
|
||||||
return nil, fmt.Errorf("Unknown API '%s'", name) |
|
||||||
} |
|
||||||
} |
|
||||||
return apis, nil |
|
||||||
} |
|
||||||
|
|
||||||
func Javascript(name string) string { |
|
||||||
switch strings.ToLower(strings.TrimSpace(name)) { |
|
||||||
case shared.AdminApiName: |
|
||||||
return Admin_JS |
|
||||||
case shared.DebugApiName: |
|
||||||
return Debug_JS |
|
||||||
case shared.DbApiName: |
|
||||||
return Db_JS |
|
||||||
case shared.EthApiName: |
|
||||||
return Eth_JS |
|
||||||
case shared.MinerApiName: |
|
||||||
return Miner_JS |
|
||||||
case shared.NetApiName: |
|
||||||
return Net_JS |
|
||||||
case shared.ShhApiName: |
|
||||||
return Shh_JS |
|
||||||
case shared.TxPoolApiName: |
|
||||||
return TxPool_JS |
|
||||||
case shared.PersonalApiName: |
|
||||||
return Personal_JS |
|
||||||
} |
|
||||||
|
|
||||||
return "" |
|
||||||
} |
|
@ -1,99 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package api |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/crypto" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
"github.com/ethereum/go-ethereum/xeth" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
Web3ApiVersion = "1.0" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// mapping between methods and handlers
|
|
||||||
Web3Mapping = map[string]web3handler{ |
|
||||||
"web3_sha3": (*web3Api).Sha3, |
|
||||||
"web3_clientVersion": (*web3Api).ClientVersion, |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
// web3 callback handler
|
|
||||||
type web3handler func(*web3Api, *shared.Request) (interface{}, error) |
|
||||||
|
|
||||||
// web3 api provider
|
|
||||||
type web3Api struct { |
|
||||||
xeth *xeth.XEth |
|
||||||
methods map[string]web3handler |
|
||||||
codec codec.ApiCoder |
|
||||||
} |
|
||||||
|
|
||||||
// create a new web3 api instance
|
|
||||||
func NewWeb3Api(xeth *xeth.XEth, coder codec.Codec) *web3Api { |
|
||||||
return &web3Api{ |
|
||||||
xeth: xeth, |
|
||||||
methods: Web3Mapping, |
|
||||||
codec: coder.New(nil), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// collection with supported methods
|
|
||||||
func (self *web3Api) Methods() []string { |
|
||||||
methods := make([]string, len(self.methods)) |
|
||||||
i := 0 |
|
||||||
for k := range self.methods { |
|
||||||
methods[i] = k |
|
||||||
i++ |
|
||||||
} |
|
||||||
return methods |
|
||||||
} |
|
||||||
|
|
||||||
// Execute given request
|
|
||||||
func (self *web3Api) Execute(req *shared.Request) (interface{}, error) { |
|
||||||
if callback, ok := self.methods[req.Method]; ok { |
|
||||||
return callback(self, req) |
|
||||||
} |
|
||||||
|
|
||||||
return nil, &shared.NotImplementedError{req.Method} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *web3Api) Name() string { |
|
||||||
return shared.Web3ApiName |
|
||||||
} |
|
||||||
|
|
||||||
func (self *web3Api) ApiVersion() string { |
|
||||||
return Web3ApiVersion |
|
||||||
} |
|
||||||
|
|
||||||
// Calculates the sha3 over req.Params.Data
|
|
||||||
func (self *web3Api) Sha3(req *shared.Request) (interface{}, error) { |
|
||||||
args := new(Sha3Args) |
|
||||||
if err := self.codec.Decode(req.Params, &args); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return common.ToHex(crypto.Sha3(common.FromHex(args.Data))), nil |
|
||||||
} |
|
||||||
|
|
||||||
// returns the xeth client vrsion
|
|
||||||
func (self *web3Api) ClientVersion(req *shared.Request) (interface{}, error) { |
|
||||||
return self.xeth.ClientVersion(), nil |
|
||||||
} |
|
@ -1,65 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package codec |
|
||||||
|
|
||||||
import ( |
|
||||||
"net" |
|
||||||
"strconv" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
type Codec int |
|
||||||
|
|
||||||
// (de)serialization support for rpc interface
|
|
||||||
type ApiCoder interface { |
|
||||||
// Parse message to request from underlying stream
|
|
||||||
ReadRequest() ([]*shared.Request, bool, error) |
|
||||||
// Parse response message from underlying stream
|
|
||||||
ReadResponse() (interface{}, error) |
|
||||||
// Read raw message from underlying stream
|
|
||||||
Recv() (interface{}, error) |
|
||||||
// Encode response to encoded form in underlying stream
|
|
||||||
WriteResponse(interface{}) error |
|
||||||
// Decode single message from data
|
|
||||||
Decode([]byte, interface{}) error |
|
||||||
// Encode msg to encoded form
|
|
||||||
Encode(msg interface{}) ([]byte, error) |
|
||||||
// close the underlying stream
|
|
||||||
Close() |
|
||||||
} |
|
||||||
|
|
||||||
// supported codecs
|
|
||||||
const ( |
|
||||||
JSON Codec = iota |
|
||||||
nCodecs |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// collection with supported coders
|
|
||||||
coders = make([]func(net.Conn) ApiCoder, nCodecs) |
|
||||||
) |
|
||||||
|
|
||||||
// create a new coder instance
|
|
||||||
func (c Codec) New(conn net.Conn) ApiCoder { |
|
||||||
switch c { |
|
||||||
case JSON: |
|
||||||
return NewJsonCoder(conn) |
|
||||||
} |
|
||||||
|
|
||||||
panic("codec: request for codec #" + strconv.Itoa(int(c)) + " is unavailable") |
|
||||||
} |
|
@ -1,149 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package codec |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
"fmt" |
|
||||||
"net" |
|
||||||
"time" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
READ_TIMEOUT = 60 // in seconds
|
|
||||||
MAX_REQUEST_SIZE = 1024 * 1024 |
|
||||||
MAX_RESPONSE_SIZE = 1024 * 1024 |
|
||||||
) |
|
||||||
|
|
||||||
// Json serialization support
|
|
||||||
type JsonCodec struct { |
|
||||||
c net.Conn |
|
||||||
d *json.Decoder |
|
||||||
} |
|
||||||
|
|
||||||
// Create new JSON coder instance
|
|
||||||
func NewJsonCoder(conn net.Conn) ApiCoder { |
|
||||||
return &JsonCodec{ |
|
||||||
c: conn, |
|
||||||
d: json.NewDecoder(conn), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Read incoming request and parse it to RPC request
|
|
||||||
func (self *JsonCodec) ReadRequest() (requests []*shared.Request, isBatch bool, err error) { |
|
||||||
deadline := time.Now().Add(READ_TIMEOUT * time.Second) |
|
||||||
if err := self.c.SetDeadline(deadline); err != nil { |
|
||||||
return nil, false, err |
|
||||||
} |
|
||||||
|
|
||||||
var incoming json.RawMessage |
|
||||||
err = self.d.Decode(&incoming) |
|
||||||
if err == nil { |
|
||||||
isBatch = incoming[0] == '[' |
|
||||||
if isBatch { |
|
||||||
requests = make([]*shared.Request, 0) |
|
||||||
err = json.Unmarshal(incoming, &requests) |
|
||||||
} else { |
|
||||||
requests = make([]*shared.Request, 1) |
|
||||||
var singleRequest shared.Request |
|
||||||
if err = json.Unmarshal(incoming, &singleRequest); err == nil { |
|
||||||
requests[0] = &singleRequest |
|
||||||
} |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
self.c.Close() |
|
||||||
return nil, false, err |
|
||||||
} |
|
||||||
|
|
||||||
func (self *JsonCodec) Recv() (interface{}, error) { |
|
||||||
var msg json.RawMessage |
|
||||||
err := self.d.Decode(&msg) |
|
||||||
if err != nil { |
|
||||||
self.c.Close() |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return msg, err |
|
||||||
} |
|
||||||
|
|
||||||
func (self *JsonCodec) ReadResponse() (interface{}, error) { |
|
||||||
in, err := self.Recv() |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
if msg, ok := in.(json.RawMessage); ok { |
|
||||||
var req *shared.Request |
|
||||||
if err = json.Unmarshal(msg, &req); err == nil && strings.HasPrefix(req.Method, "agent_") { |
|
||||||
return req, nil |
|
||||||
} |
|
||||||
|
|
||||||
var failure *shared.ErrorResponse |
|
||||||
if err = json.Unmarshal(msg, &failure); err == nil && failure.Error != nil { |
|
||||||
return failure, fmt.Errorf(failure.Error.Message) |
|
||||||
} |
|
||||||
|
|
||||||
var success *shared.SuccessResponse |
|
||||||
if err = json.Unmarshal(msg, &success); err == nil { |
|
||||||
return success, nil |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return in, err |
|
||||||
} |
|
||||||
|
|
||||||
// Decode data
|
|
||||||
func (self *JsonCodec) Decode(data []byte, msg interface{}) error { |
|
||||||
return json.Unmarshal(data, msg) |
|
||||||
} |
|
||||||
|
|
||||||
// Encode message
|
|
||||||
func (self *JsonCodec) Encode(msg interface{}) ([]byte, error) { |
|
||||||
return json.Marshal(msg) |
|
||||||
} |
|
||||||
|
|
||||||
// Parse JSON data from conn to obj
|
|
||||||
func (self *JsonCodec) WriteResponse(res interface{}) error { |
|
||||||
data, err := json.Marshal(res) |
|
||||||
if err != nil { |
|
||||||
self.c.Close() |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
bytesWritten := 0 |
|
||||||
|
|
||||||
for bytesWritten < len(data) { |
|
||||||
n, err := self.c.Write(data[bytesWritten:]) |
|
||||||
if err != nil { |
|
||||||
self.c.Close() |
|
||||||
return err |
|
||||||
} |
|
||||||
bytesWritten += n |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// Close decoder and encoder
|
|
||||||
func (self *JsonCodec) Close() { |
|
||||||
self.c.Close() |
|
||||||
} |
|
@ -1,157 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package codec |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"io" |
|
||||||
"net" |
|
||||||
"testing" |
|
||||||
"time" |
|
||||||
) |
|
||||||
|
|
||||||
type jsonTestConn struct { |
|
||||||
buffer *bytes.Buffer |
|
||||||
} |
|
||||||
|
|
||||||
func newJsonTestConn(data []byte) *jsonTestConn { |
|
||||||
return &jsonTestConn{ |
|
||||||
buffer: bytes.NewBuffer(data), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *jsonTestConn) Read(p []byte) (n int, err error) { |
|
||||||
return self.buffer.Read(p) |
|
||||||
} |
|
||||||
|
|
||||||
func (self *jsonTestConn) Write(p []byte) (n int, err error) { |
|
||||||
return self.buffer.Write(p) |
|
||||||
} |
|
||||||
|
|
||||||
func (self *jsonTestConn) Close() error { |
|
||||||
// not implemented
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *jsonTestConn) LocalAddr() net.Addr { |
|
||||||
// not implemented
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *jsonTestConn) RemoteAddr() net.Addr { |
|
||||||
// not implemented
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *jsonTestConn) SetDeadline(t time.Time) error { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *jsonTestConn) SetReadDeadline(t time.Time) error { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *jsonTestConn) SetWriteDeadline(t time.Time) error { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func TestJsonDecoderWithValidRequest(t *testing.T) { |
|
||||||
reqdata := []byte(`{"jsonrpc":"2.0","method":"modules","params":[],"id":64}`) |
|
||||||
decoder := newJsonTestConn(reqdata) |
|
||||||
|
|
||||||
jsonDecoder := NewJsonCoder(decoder) |
|
||||||
requests, batch, err := jsonDecoder.ReadRequest() |
|
||||||
|
|
||||||
if err != nil { |
|
||||||
t.Errorf("Read valid request failed - %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
if len(requests) != 1 { |
|
||||||
t.Errorf("Expected to get a single request but got %d", len(requests)) |
|
||||||
} |
|
||||||
|
|
||||||
if batch { |
|
||||||
t.Errorf("Got batch indication while expecting single request") |
|
||||||
} |
|
||||||
|
|
||||||
if requests[0].Id != float64(64) { |
|
||||||
t.Errorf("Expected req.Id == 64 but got %v", requests[0].Id) |
|
||||||
} |
|
||||||
|
|
||||||
if requests[0].Method != "modules" { |
|
||||||
t.Errorf("Expected req.Method == 'modules' got '%s'", requests[0].Method) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestJsonDecoderWithValidBatchRequest(t *testing.T) { |
|
||||||
reqdata := []byte(`[{"jsonrpc":"2.0","method":"modules","params":[],"id":64}, |
|
||||||
{"jsonrpc":"2.0","method":"modules","params":[],"id":64}]`) |
|
||||||
decoder := newJsonTestConn(reqdata) |
|
||||||
|
|
||||||
jsonDecoder := NewJsonCoder(decoder) |
|
||||||
requests, batch, err := jsonDecoder.ReadRequest() |
|
||||||
|
|
||||||
if err != nil { |
|
||||||
t.Errorf("Read valid batch request failed - %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
if len(requests) != 2 { |
|
||||||
t.Errorf("Expected to get two requests but got %d", len(requests)) |
|
||||||
} |
|
||||||
|
|
||||||
if !batch { |
|
||||||
t.Errorf("Got no batch indication while expecting batch request") |
|
||||||
} |
|
||||||
|
|
||||||
for i := 0; i < len(requests); i++ { |
|
||||||
if requests[i].Id != float64(64) { |
|
||||||
t.Errorf("Expected req.Id == 64 but got %v", requests[i].Id) |
|
||||||
} |
|
||||||
|
|
||||||
if requests[i].Method != "modules" { |
|
||||||
t.Errorf("Expected req.Method == 'modules' got '%s'", requests[i].Method) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestJsonDecoderWithInvalidIncompleteMessage(t *testing.T) { |
|
||||||
reqdata := []byte(`{"jsonrpc":"2.0","method":"modules","pa`) |
|
||||||
decoder := newJsonTestConn(reqdata) |
|
||||||
|
|
||||||
jsonDecoder := NewJsonCoder(decoder) |
|
||||||
requests, batch, err := jsonDecoder.ReadRequest() |
|
||||||
|
|
||||||
if err != io.ErrUnexpectedEOF { |
|
||||||
t.Errorf("Expected to read an incomplete request err but got %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
// remaining message
|
|
||||||
decoder.Write([]byte(`rams":[],"id:64"}`)) |
|
||||||
requests, batch, err = jsonDecoder.ReadRequest() |
|
||||||
|
|
||||||
if err == nil { |
|
||||||
t.Errorf("Expected an error but got nil") |
|
||||||
} |
|
||||||
|
|
||||||
if len(requests) != 0 { |
|
||||||
t.Errorf("Expected to get no requests but got %d", len(requests)) |
|
||||||
} |
|
||||||
|
|
||||||
if batch { |
|
||||||
t.Errorf("Got batch indication while expecting non batch") |
|
||||||
} |
|
||||||
} |
|
@ -1,150 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package comms |
|
||||||
|
|
||||||
import ( |
|
||||||
"io" |
|
||||||
"net" |
|
||||||
|
|
||||||
"fmt" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"strconv" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/logger" |
|
||||||
"github.com/ethereum/go-ethereum/logger/glog" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
maxHttpSizeReqLength = 1024 * 1024 // 1MB
|
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// List with all API's which are offered over the in proc interface by default
|
|
||||||
DefaultInProcApis = shared.AllApis |
|
||||||
|
|
||||||
// List with all API's which are offered over the IPC interface by default
|
|
||||||
DefaultIpcApis = shared.AllApis |
|
||||||
|
|
||||||
// List with API's which are offered over thr HTTP/RPC interface by default
|
|
||||||
DefaultHttpRpcApis = strings.Join([]string{ |
|
||||||
shared.DbApiName, shared.EthApiName, shared.NetApiName, shared.Web3ApiName, |
|
||||||
}, ",") |
|
||||||
) |
|
||||||
|
|
||||||
type EthereumClient interface { |
|
||||||
// Close underlying connection
|
|
||||||
Close() |
|
||||||
// Send request
|
|
||||||
Send(interface{}) error |
|
||||||
// Receive response
|
|
||||||
Recv() (interface{}, error) |
|
||||||
// List with modules this client supports
|
|
||||||
SupportedModules() (map[string]string, error) |
|
||||||
} |
|
||||||
|
|
||||||
func handle(id int, conn net.Conn, api shared.EthereumApi, c codec.Codec) { |
|
||||||
codec := c.New(conn) |
|
||||||
|
|
||||||
defer func() { |
|
||||||
if r := recover(); r != nil { |
|
||||||
glog.Errorf("panic: %v\n", r) |
|
||||||
} |
|
||||||
codec.Close() |
|
||||||
}() |
|
||||||
|
|
||||||
for { |
|
||||||
requests, isBatch, err := codec.ReadRequest() |
|
||||||
if err == io.EOF { |
|
||||||
return |
|
||||||
} else if err != nil { |
|
||||||
glog.V(logger.Debug).Infof("Closed IPC Conn %06d recv err - %v\n", id, err) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
if isBatch { |
|
||||||
responses := make([]*interface{}, len(requests)) |
|
||||||
responseCount := 0 |
|
||||||
for _, req := range requests { |
|
||||||
res, err := api.Execute(req) |
|
||||||
if req.Id != nil { |
|
||||||
rpcResponse := shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err) |
|
||||||
responses[responseCount] = rpcResponse |
|
||||||
responseCount += 1 |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
err = codec.WriteResponse(responses[:responseCount]) |
|
||||||
if err != nil { |
|
||||||
glog.V(logger.Debug).Infof("Closed IPC Conn %06d send err - %v\n", id, err) |
|
||||||
return |
|
||||||
} |
|
||||||
} else { |
|
||||||
var rpcResponse interface{} |
|
||||||
res, err := api.Execute(requests[0]) |
|
||||||
|
|
||||||
rpcResponse = shared.NewRpcResponse(requests[0].Id, requests[0].Jsonrpc, res, err) |
|
||||||
err = codec.WriteResponse(rpcResponse) |
|
||||||
if err != nil { |
|
||||||
glog.V(logger.Debug).Infof("Closed IPC Conn %06d send err - %v\n", id, err) |
|
||||||
return |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Endpoint must be in the form of:
|
|
||||||
// ${protocol}:${path}
|
|
||||||
// e.g. ipc:/tmp/geth.ipc
|
|
||||||
// rpc:localhost:8545
|
|
||||||
func ClientFromEndpoint(endpoint string, c codec.Codec) (EthereumClient, error) { |
|
||||||
if strings.HasPrefix(endpoint, "ipc:") { |
|
||||||
cfg := IpcConfig{ |
|
||||||
Endpoint: endpoint[4:], |
|
||||||
} |
|
||||||
return NewIpcClient(cfg, codec.JSON) |
|
||||||
} |
|
||||||
|
|
||||||
if strings.HasPrefix(endpoint, "rpc:") { |
|
||||||
parts := strings.Split(endpoint, ":") |
|
||||||
addr := "http://localhost" |
|
||||||
port := uint(8545) |
|
||||||
if len(parts) >= 3 { |
|
||||||
addr = parts[1] + ":" + parts[2] |
|
||||||
} |
|
||||||
|
|
||||||
if len(parts) >= 4 { |
|
||||||
p, err := strconv.Atoi(parts[3]) |
|
||||||
|
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
port = uint(p) |
|
||||||
} |
|
||||||
|
|
||||||
cfg := HttpConfig{ |
|
||||||
ListenAddress: addr, |
|
||||||
ListenPort: port, |
|
||||||
} |
|
||||||
|
|
||||||
return NewHttpClient(cfg, codec.JSON), nil |
|
||||||
} |
|
||||||
|
|
||||||
return nil, fmt.Errorf("Invalid endpoint") |
|
||||||
} |
|
@ -1,345 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package comms |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
"fmt" |
|
||||||
"net" |
|
||||||
"net/http" |
|
||||||
"strings" |
|
||||||
"sync" |
|
||||||
"time" |
|
||||||
|
|
||||||
"bytes" |
|
||||||
"io" |
|
||||||
"io/ioutil" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/logger" |
|
||||||
"github.com/ethereum/go-ethereum/logger/glog" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
"github.com/rs/cors" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
serverIdleTimeout = 10 * time.Second // idle keep-alive connections
|
|
||||||
serverReadTimeout = 15 * time.Second // per-request read timeout
|
|
||||||
serverWriteTimeout = 15 * time.Second // per-request read timeout
|
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
httpServerMu sync.Mutex |
|
||||||
httpServer *stopServer |
|
||||||
) |
|
||||||
|
|
||||||
type HttpConfig struct { |
|
||||||
ListenAddress string |
|
||||||
ListenPort uint |
|
||||||
CorsDomain string |
|
||||||
} |
|
||||||
|
|
||||||
// stopServer augments http.Server with idle connection tracking.
|
|
||||||
// Idle keep-alive connections are shut down when Close is called.
|
|
||||||
type stopServer struct { |
|
||||||
*http.Server |
|
||||||
l net.Listener |
|
||||||
// connection tracking state
|
|
||||||
mu sync.Mutex |
|
||||||
shutdown bool // true when Stop has returned
|
|
||||||
idle map[net.Conn]struct{} |
|
||||||
} |
|
||||||
|
|
||||||
type handler struct { |
|
||||||
codec codec.Codec |
|
||||||
api shared.EthereumApi |
|
||||||
} |
|
||||||
|
|
||||||
// StartHTTP starts listening for RPC requests sent via HTTP.
|
|
||||||
func StartHttp(cfg HttpConfig, codec codec.Codec, api shared.EthereumApi) error { |
|
||||||
httpServerMu.Lock() |
|
||||||
defer httpServerMu.Unlock() |
|
||||||
|
|
||||||
addr := fmt.Sprintf("%s:%d", cfg.ListenAddress, cfg.ListenPort) |
|
||||||
if httpServer != nil { |
|
||||||
if addr != httpServer.Addr { |
|
||||||
return fmt.Errorf("RPC service already running on %s ", httpServer.Addr) |
|
||||||
} |
|
||||||
return nil // RPC service already running on given host/port
|
|
||||||
} |
|
||||||
// Set up the request handler, wrapping it with CORS headers if configured.
|
|
||||||
handler := http.Handler(&handler{codec, api}) |
|
||||||
if len(cfg.CorsDomain) > 0 { |
|
||||||
opts := cors.Options{ |
|
||||||
AllowedMethods: []string{"POST"}, |
|
||||||
AllowedOrigins: strings.Split(cfg.CorsDomain, " "), |
|
||||||
} |
|
||||||
handler = cors.New(opts).Handler(handler) |
|
||||||
} |
|
||||||
// Start the server.
|
|
||||||
s, err := listenHTTP(addr, handler) |
|
||||||
if err != nil { |
|
||||||
glog.V(logger.Error).Infof("Can't listen on %s:%d: %v", cfg.ListenAddress, cfg.ListenPort, err) |
|
||||||
return err |
|
||||||
} |
|
||||||
httpServer = s |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { |
|
||||||
w.Header().Set("Content-Type", "application/json") |
|
||||||
|
|
||||||
// Limit request size to resist DoS
|
|
||||||
if req.ContentLength > maxHttpSizeReqLength { |
|
||||||
err := fmt.Errorf("Request too large") |
|
||||||
response := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32700, err) |
|
||||||
sendJSON(w, &response) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
defer req.Body.Close() |
|
||||||
payload, err := ioutil.ReadAll(req.Body) |
|
||||||
if err != nil { |
|
||||||
err := fmt.Errorf("Could not read request body") |
|
||||||
response := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32700, err) |
|
||||||
sendJSON(w, &response) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
c := h.codec.New(nil) |
|
||||||
var rpcReq shared.Request |
|
||||||
if err = c.Decode(payload, &rpcReq); err == nil { |
|
||||||
reply, err := h.api.Execute(&rpcReq) |
|
||||||
res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err) |
|
||||||
sendJSON(w, &res) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
var reqBatch []shared.Request |
|
||||||
if err = c.Decode(payload, &reqBatch); err == nil { |
|
||||||
resBatch := make([]*interface{}, len(reqBatch)) |
|
||||||
resCount := 0 |
|
||||||
for i, rpcReq := range reqBatch { |
|
||||||
reply, err := h.api.Execute(&rpcReq) |
|
||||||
if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal
|
|
||||||
resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err) |
|
||||||
resCount += 1 |
|
||||||
} |
|
||||||
} |
|
||||||
// make response omitting nil entries
|
|
||||||
sendJSON(w, resBatch[:resCount]) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// invalid request
|
|
||||||
err = fmt.Errorf("Could not decode request") |
|
||||||
res := shared.NewRpcErrorResponse(-1, shared.JsonRpcVersion, -32600, err) |
|
||||||
sendJSON(w, res) |
|
||||||
} |
|
||||||
|
|
||||||
func sendJSON(w io.Writer, v interface{}) { |
|
||||||
if glog.V(logger.Detail) { |
|
||||||
if payload, err := json.MarshalIndent(v, "", "\t"); err == nil { |
|
||||||
glog.Infof("Sending payload: %s", payload) |
|
||||||
} |
|
||||||
} |
|
||||||
if err := json.NewEncoder(w).Encode(v); err != nil { |
|
||||||
glog.V(logger.Error).Infoln("Error sending JSON:", err) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Stop closes all active HTTP connections and shuts down the server.
|
|
||||||
func StopHttp() { |
|
||||||
httpServerMu.Lock() |
|
||||||
defer httpServerMu.Unlock() |
|
||||||
if httpServer != nil { |
|
||||||
httpServer.Close() |
|
||||||
httpServer = nil |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func listenHTTP(addr string, h http.Handler) (*stopServer, error) { |
|
||||||
l, err := net.Listen("tcp", addr) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
s := &stopServer{l: l, idle: make(map[net.Conn]struct{})} |
|
||||||
s.Server = &http.Server{ |
|
||||||
Addr: addr, |
|
||||||
Handler: h, |
|
||||||
ReadTimeout: serverReadTimeout, |
|
||||||
WriteTimeout: serverWriteTimeout, |
|
||||||
ConnState: s.connState, |
|
||||||
} |
|
||||||
go s.Serve(l) |
|
||||||
return s, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (s *stopServer) connState(c net.Conn, state http.ConnState) { |
|
||||||
s.mu.Lock() |
|
||||||
defer s.mu.Unlock() |
|
||||||
// Close c immediately if we're past shutdown.
|
|
||||||
if s.shutdown { |
|
||||||
if state != http.StateClosed { |
|
||||||
c.Close() |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
if state == http.StateIdle { |
|
||||||
s.idle[c] = struct{}{} |
|
||||||
} else { |
|
||||||
delete(s.idle, c) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (s *stopServer) Close() { |
|
||||||
s.mu.Lock() |
|
||||||
defer s.mu.Unlock() |
|
||||||
// Shut down the acceptor. No new connections can be created.
|
|
||||||
s.l.Close() |
|
||||||
// Drop all idle connections. Non-idle connections will be
|
|
||||||
// closed by connState as soon as they become idle.
|
|
||||||
s.shutdown = true |
|
||||||
for c := range s.idle { |
|
||||||
glog.V(logger.Detail).Infof("closing idle connection %v", c.RemoteAddr()) |
|
||||||
c.Close() |
|
||||||
delete(s.idle, c) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
type httpClient struct { |
|
||||||
address string |
|
||||||
port uint |
|
||||||
codec codec.ApiCoder |
|
||||||
lastRes interface{} |
|
||||||
lastErr error |
|
||||||
} |
|
||||||
|
|
||||||
// Create a new in process client
|
|
||||||
func NewHttpClient(cfg HttpConfig, c codec.Codec) *httpClient { |
|
||||||
return &httpClient{ |
|
||||||
address: cfg.ListenAddress, |
|
||||||
port: cfg.ListenPort, |
|
||||||
codec: c.New(nil), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *httpClient) Close() { |
|
||||||
// do nothing
|
|
||||||
} |
|
||||||
|
|
||||||
func (self *httpClient) Send(req interface{}) error { |
|
||||||
var body []byte |
|
||||||
var err error |
|
||||||
|
|
||||||
self.lastRes = nil |
|
||||||
self.lastErr = nil |
|
||||||
|
|
||||||
if body, err = self.codec.Encode(req); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", fmt.Sprintf("%s:%d", self.address, self.port), bytes.NewBuffer(body)) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
httpReq.Header.Set("Content-Type", "application/json") |
|
||||||
|
|
||||||
client := http.Client{} |
|
||||||
resp, err := client.Do(httpReq) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
defer resp.Body.Close() |
|
||||||
|
|
||||||
if resp.Status == "200 OK" { |
|
||||||
reply, _ := ioutil.ReadAll(resp.Body) |
|
||||||
var rpcSuccessResponse shared.SuccessResponse |
|
||||||
if err = self.codec.Decode(reply, &rpcSuccessResponse); err == nil { |
|
||||||
self.lastRes = &rpcSuccessResponse |
|
||||||
self.lastErr = err |
|
||||||
return nil |
|
||||||
} else { |
|
||||||
var rpcErrorResponse shared.ErrorResponse |
|
||||||
if err = self.codec.Decode(reply, &rpcErrorResponse); err == nil { |
|
||||||
self.lastRes = &rpcErrorResponse |
|
||||||
self.lastErr = err |
|
||||||
return nil |
|
||||||
} else { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return fmt.Errorf("Not implemented") |
|
||||||
} |
|
||||||
|
|
||||||
func (self *httpClient) Recv() (interface{}, error) { |
|
||||||
return self.lastRes, self.lastErr |
|
||||||
} |
|
||||||
|
|
||||||
func (self *httpClient) SupportedModules() (map[string]string, error) { |
|
||||||
var body []byte |
|
||||||
var err error |
|
||||||
|
|
||||||
payload := shared.Request{ |
|
||||||
Id: 1, |
|
||||||
Jsonrpc: "2.0", |
|
||||||
Method: "modules", |
|
||||||
} |
|
||||||
|
|
||||||
if body, err = self.codec.Encode(payload); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", fmt.Sprintf("%s:%d", self.address, self.port), bytes.NewBuffer(body)) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
req.Header.Set("Content-Type", "application/json") |
|
||||||
|
|
||||||
client := http.Client{} |
|
||||||
resp, err := client.Do(req) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
defer resp.Body.Close() |
|
||||||
|
|
||||||
if resp.Status == "200 OK" { |
|
||||||
reply, _ := ioutil.ReadAll(resp.Body) |
|
||||||
var rpcRes shared.SuccessResponse |
|
||||||
if err = self.codec.Decode(reply, &rpcRes); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
result := make(map[string]string) |
|
||||||
if modules, ok := rpcRes.Result.(map[string]interface{}); ok { |
|
||||||
for a, v := range modules { |
|
||||||
result[a] = fmt.Sprintf("%s", v) |
|
||||||
} |
|
||||||
return result, nil |
|
||||||
} |
|
||||||
err = fmt.Errorf("Unable to parse module response - %v", rpcRes.Result) |
|
||||||
} else { |
|
||||||
fmt.Printf("resp.Status = %s\n", resp.Status) |
|
||||||
fmt.Printf("err = %v\n", err) |
|
||||||
} |
|
||||||
|
|
||||||
return nil, err |
|
||||||
} |
|
@ -1,82 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package comms |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
type InProcClient struct { |
|
||||||
api shared.EthereumApi |
|
||||||
codec codec.Codec |
|
||||||
lastId interface{} |
|
||||||
lastJsonrpc string |
|
||||||
lastErr error |
|
||||||
lastRes interface{} |
|
||||||
} |
|
||||||
|
|
||||||
// Create a new in process client
|
|
||||||
func NewInProcClient(codec codec.Codec) *InProcClient { |
|
||||||
return &InProcClient{ |
|
||||||
codec: codec, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *InProcClient) Close() { |
|
||||||
// do nothing
|
|
||||||
} |
|
||||||
|
|
||||||
// Need to setup api support
|
|
||||||
func (self *InProcClient) Initialize(offeredApi shared.EthereumApi) { |
|
||||||
self.api = offeredApi |
|
||||||
} |
|
||||||
|
|
||||||
func (self *InProcClient) Send(req interface{}) error { |
|
||||||
if r, ok := req.(*shared.Request); ok { |
|
||||||
self.lastId = r.Id |
|
||||||
self.lastJsonrpc = r.Jsonrpc |
|
||||||
self.lastRes, self.lastErr = self.api.Execute(r) |
|
||||||
return self.lastErr |
|
||||||
} |
|
||||||
|
|
||||||
return fmt.Errorf("Invalid request (%T)", req) |
|
||||||
} |
|
||||||
|
|
||||||
func (self *InProcClient) Recv() (interface{}, error) { |
|
||||||
return *shared.NewRpcResponse(self.lastId, self.lastJsonrpc, self.lastRes, self.lastErr), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *InProcClient) SupportedModules() (map[string]string, error) { |
|
||||||
req := shared.Request{ |
|
||||||
Id: 1, |
|
||||||
Jsonrpc: "2.0", |
|
||||||
Method: "modules", |
|
||||||
} |
|
||||||
|
|
||||||
if res, err := self.api.Execute(&req); err == nil { |
|
||||||
if result, ok := res.(map[string]string); ok { |
|
||||||
return result, nil |
|
||||||
} |
|
||||||
} else { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return nil, fmt.Errorf("Invalid response") |
|
||||||
} |
|
@ -1,158 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package comms |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"math/rand" |
|
||||||
"net" |
|
||||||
"os" |
|
||||||
|
|
||||||
"encoding/json" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/logger" |
|
||||||
"github.com/ethereum/go-ethereum/logger/glog" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/codec" |
|
||||||
"github.com/ethereum/go-ethereum/rpc/shared" |
|
||||||
) |
|
||||||
|
|
||||||
type Stopper interface { |
|
||||||
Stop() |
|
||||||
} |
|
||||||
|
|
||||||
type InitFunc func(conn net.Conn) (Stopper, shared.EthereumApi, error) |
|
||||||
|
|
||||||
type IpcConfig struct { |
|
||||||
Endpoint string |
|
||||||
} |
|
||||||
|
|
||||||
type ipcClient struct { |
|
||||||
endpoint string |
|
||||||
c net.Conn |
|
||||||
codec codec.Codec |
|
||||||
coder codec.ApiCoder |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ipcClient) Close() { |
|
||||||
self.coder.Close() |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ipcClient) Send(msg interface{}) error { |
|
||||||
var err error |
|
||||||
if err = self.coder.WriteResponse(msg); err != nil { |
|
||||||
if err = self.reconnect(); err == nil { |
|
||||||
err = self.coder.WriteResponse(msg) |
|
||||||
} |
|
||||||
} |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ipcClient) Recv() (interface{}, error) { |
|
||||||
return self.coder.ReadResponse() |
|
||||||
} |
|
||||||
|
|
||||||
func (self *ipcClient) SupportedModules() (map[string]string, error) { |
|
||||||
req := shared.Request{ |
|
||||||
Id: 1, |
|
||||||
Jsonrpc: "2.0", |
|
||||||
Method: "rpc_modules", |
|
||||||
} |
|
||||||
|
|
||||||
if err := self.coder.WriteResponse(req); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
res, _ := self.coder.ReadResponse() |
|
||||||
if sucRes, ok := res.(*shared.SuccessResponse); ok { |
|
||||||
data, _ := json.Marshal(sucRes.Result) |
|
||||||
modules := make(map[string]string) |
|
||||||
if err := json.Unmarshal(data, &modules); err == nil { |
|
||||||
return modules, nil |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// old version uses modules instead of rpc_modules, this can be removed after full migration
|
|
||||||
req.Method = "modules" |
|
||||||
if err := self.coder.WriteResponse(req); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
res, err := self.coder.ReadResponse() |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
if sucRes, ok := res.(*shared.SuccessResponse); ok { |
|
||||||
data, _ := json.Marshal(sucRes.Result) |
|
||||||
modules := make(map[string]string) |
|
||||||
err = json.Unmarshal(data, &modules) |
|
||||||
if err == nil { |
|
||||||
return modules, nil |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil, fmt.Errorf("Invalid response") |
|
||||||
} |
|
||||||
|
|
||||||
// Create a new IPC client, UNIX domain socket on posix, named pipe on Windows
|
|
||||||
func NewIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { |
|
||||||
return newIpcClient(cfg, codec) |
|
||||||
} |
|
||||||
|
|
||||||
// Start IPC server
|
|
||||||
func StartIpc(cfg IpcConfig, codec codec.Codec, initializer InitFunc) error { |
|
||||||
l, err := ipcListen(cfg) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
go ipcLoop(cfg, codec, initializer, l) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// CreateListener creates an listener, on Unix platforms this is a unix socket, on Windows this is a named pipe
|
|
||||||
func CreateListener(cfg IpcConfig) (net.Listener, error) { |
|
||||||
return ipcListen(cfg) |
|
||||||
} |
|
||||||
|
|
||||||
func ipcLoop(cfg IpcConfig, codec codec.Codec, initializer InitFunc, l net.Listener) { |
|
||||||
glog.V(logger.Info).Infof("IPC service started (%s)\n", cfg.Endpoint) |
|
||||||
defer os.Remove(cfg.Endpoint) |
|
||||||
defer l.Close() |
|
||||||
for { |
|
||||||
conn, err := l.Accept() |
|
||||||
if err != nil { |
|
||||||
glog.V(logger.Debug).Infof("accept: %v", err) |
|
||||||
return |
|
||||||
} |
|
||||||
id := newIpcConnId() |
|
||||||
go func() { |
|
||||||
defer conn.Close() |
|
||||||
glog.V(logger.Debug).Infof("new connection with id %06d started", id) |
|
||||||
stopper, api, err := initializer(conn) |
|
||||||
if err != nil { |
|
||||||
glog.V(logger.Error).Infof("Unable to initialize IPC connection: %v", err) |
|
||||||
return |
|
||||||
} |
|
||||||
defer stopper.Stop() |
|
||||||
handle(id, conn, api, codec) |
|
||||||
}() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func newIpcConnId() int { |
|
||||||
return rand.Int() % 1000000 |
|
||||||
} |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue