Skip to content

Instantly share code, notes, and snippets.

@orian

orian/main.go Secret

Created October 13, 2014 17:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save orian/f766fdf561aae15997e3 to your computer and use it in GitHub Desktop.
Save orian/f766fdf561aae15997e3 to your computer and use it in GitHub Desktop.
Example use of net/rpc/jsonrpc Golang codec.
package main
import (
"bufio"
"errors"
"flag"
"fmt"
"io"
"log"
"net"
"net/http"
"net/rpc"
"net/rpc/jsonrpc"
)
// From: https://code.google.com/p/go/source/browse/src/net/rpc/server.go#677
var connected = "200 Connected to Go RPC"
// From: https://code.google.com/p/go/source/browse/src/net/rpc/client.go#242
// Modified return type so it doesn't create a client.
func NewConnection(network, address, path string) (net.Conn, error) {
// Modified:
var err error
conn, err := net.Dial(network, address)
if err != nil {
return nil, err
}
if path == "" {
path = rpc.DefaultRPCPath
}
io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n\n")
// Require successful HTTP response
// before switching to RPC protocol.
resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"})
if err == nil && resp.Status == connected {
return conn, nil
}
if err == nil {
err = errors.New("unexpected HTTP response: " + resp.Status)
}
conn.Close()
return nil, &net.OpError{
Op: "dial-http",
Net: network + " " + address,
Addr: nil,
Err: err,
}
}
// type ClientCodecFactory func(io.ReadWriteCloser) rpc.ClientCodec
type CodecFactory func(conn io.ReadWriteCloser) rpc.ServerCodec
type RpcServerWithCodec struct {
*rpc.Server
codecFactory CodecFactory
}
func NewRpcServerWithCodec(codecFactory CodecFactory) *RpcServerWithCodec {
return &RpcServerWithCodec{rpc.NewServer(), codecFactory}
}
// From: https://code.google.com/p/go/source/browse/src/net/rpc/server.go#680
func (server *RpcServerWithCodec) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.Method != "CONNECT" {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusMethodNotAllowed)
io.WriteString(w, "405 must CONNECT\n")
return
}
conn, _, err := w.(http.Hijacker).Hijack()
if err != nil {
log.Print("rpc hijacking ", req.RemoteAddr, ": ", err.Error())
return
}
io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n")
server.ServeCodec(server.codecFactory(conn))
}
const RpcPath = "/testrpc"
type Service int
func (s *Service) Greet(name string, resp *string) error {
*resp = fmt.Sprintf("hello: %v", name)
return nil
}
func main() {
var isServer = flag.Bool("is_server", false, "Run as server? (Otherwise runs as client.)")
flag.Parse()
if *isServer {
log.Printf("Running as server.\n")
s := NewRpcServerWithCodec(jsonrpc.NewServerCodec)
svc := new(Service)
s.Register(svc)
http.Handle(RpcPath, s)
// http.Serve(l, handler)
log.Fatal(http.ListenAndServe(":1234", nil))
} else {
log.Printf("Running as client.\n")
conn, err := NewConnection("tcp", "localhost:1234", RpcPath)
if err != nil {
log.Fatalf("err: %v", err)
}
client := jsonrpc.NewClient(conn)
defer client.Close()
var resp string
err = client.Call("Service.Greet", "Alfa_11", &resp)
if err != nil {
log.Fatalf("Error when rpc-ing, %v", err)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment