mirror of https://github.com/ethereum/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 |
||||
} |
@ -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/>.
|
||||
|
||||
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||
|
||||
package comms |
||||
|
||||
import ( |
||||
"net" |
||||
"os" |
||||
"path/filepath" |
||||
|
||||
"github.com/ethereum/go-ethereum/rpc/codec" |
||||
"github.com/ethereum/go-ethereum/rpc/shared" |
||||
"github.com/ethereum/go-ethereum/rpc/useragent" |
||||
) |
||||
|
||||
func newIpcClient(cfg IpcConfig, codec codec.Codec) (*ipcClient, error) { |
||||
c, err := net.DialUnix("unix", nil, &net.UnixAddr{cfg.Endpoint, "unix"}) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
coder := codec.New(c) |
||||
msg := shared.Request{ |
||||
Id: 0, |
||||
Method: useragent.EnableUserAgentMethod, |
||||
Jsonrpc: shared.JsonRpcVersion, |
||||
Params: []byte("[]"), |
||||
} |
||||
|
||||
coder.WriteResponse(msg) |
||||
coder.Recv() |
||||
|
||||
return &ipcClient{cfg.Endpoint, c, codec, coder}, nil |
||||
} |
||||
|
||||
func (self *ipcClient) reconnect() error { |
||||
self.coder.Close() |
||||
c, err := net.DialUnix("unix", nil, &net.UnixAddr{self.endpoint, "unix"}) |
||||
if err == nil { |
||||
self.coder = self.codec.New(c) |
||||
|
||||
msg := shared.Request{ |
||||
Id: 0, |
||||
Method: useragent.EnableUserAgentMethod, |
||||
Jsonrpc: shared.JsonRpcVersion, |
||||
Params: []byte("[]"), |
||||
} |
||||
self.coder.WriteResponse(msg) |
||||
self.coder.Recv() |
||||
} |
||||
|
||||
return err |
||||
} |
||||
|
||||
func ipcListen(cfg IpcConfig) (net.Listener, error) { |
||||
// Ensure the IPC path exists and remove any previous leftover
|
||||
if err := os.MkdirAll(filepath.Dir(cfg.Endpoint), 0751); err != nil { |
||||
return nil, err |
||||
} |
||||
os.Remove(cfg.Endpoint) |
||||
l, err := net.Listen("unix", cfg.Endpoint) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
os.Chmod(cfg.Endpoint, 0600) |
||||
return l, nil |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue