qpid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From acon...@apache.org
Subject [5/5] qpid-proton git commit: PROTON-827: Initial work on Go language client for proton.
Date Fri, 10 Apr 2015 19:52:28 GMT
PROTON-827: Initial work on Go language client for proton.

This is the beginning of a Go language client for proton. There are 2 Go modules, with
rudimentary documentation;

qpid.apache.org/proton: AMQP/Go data type mapping, message encoding/decoding.
qpid.apache.org/proton/event: Go proton event API, pump proton event loop from Go connections.

examples/go/event/broker.go is a simple broker-like example, ported from examples/python/broker.py.
You can use examples/python/simple_send.py and simple_recv.py to see it in action.

examples/go/exampe.go is a very incomplete, speculative idea about the future Go-friendly user API.

The event API is not intended to be the final user API, it is a foundation for developing
that API in Go.

See proton-c/bindings/go/README.md for more.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/d6f1b837
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/d6f1b837
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/d6f1b837

Branch: refs/heads/master
Commit: d6f1b8371d0dca82531b363f2a2ebdee55e56dfa
Parents: c7c26c6
Author: Alan Conway <aconway@redhat.com>
Authored: Thu Apr 9 17:40:11 2015 -0400
Committer: Alan Conway <aconway@redhat.com>
Committed: Fri Apr 10 15:46:53 2015 -0400

----------------------------------------------------------------------
 examples/go/event/broker.go                     | 205 ++++++++
 examples/go/example.go                          |  54 ++
 proton-c/bindings/go/README.md                  | 215 ++++----
 proton-c/bindings/go/src/Makefile               |  25 +
 proton-c/bindings/go/src/genwrap.go             | 398 +++++++++++++++
 .../go/src/qpid.apache.org/proton/doc.go        |   3 +
 .../go/src/qpid.apache.org/proton/dummy.go      |  45 ++
 .../go/src/qpid.apache.org/proton/error.go      |  39 +-
 .../go/src/qpid.apache.org/proton/event/doc.go  |  28 +
 .../src/qpid.apache.org/proton/event/error.go   |  77 +++
 .../qpid.apache.org/proton/event/handlers.go    | 328 ++++++++++++
 .../go/src/qpid.apache.org/proton/event/pump.go | 262 ++++++++++
 .../qpid.apache.org/proton/event/wrappers.go    | 137 +++++
 .../proton/event/wrappers_gen.go                | 505 +++++++++++++++++++
 .../src/qpid.apache.org/proton/interop_test.go  |  48 +-
 .../go/src/qpid.apache.org/proton/marshal.go    | 105 ++--
 .../go/src/qpid.apache.org/proton/message.go    | 388 ++++++++++++++
 .../src/qpid.apache.org/proton/message_test.go  |  90 ++++
 .../go/src/qpid.apache.org/proton/types.go      | 194 ++++---
 .../go/src/qpid.apache.org/proton/unfinished.go |  53 --
 .../go/src/qpid.apache.org/proton/unmarshal.go  |  73 +--
 .../go/src/qpid.apache.org/proton/url.go        |   1 -
 22 files changed, 2956 insertions(+), 317 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/examples/go/event/broker.go
----------------------------------------------------------------------
diff --git a/examples/go/event/broker.go b/examples/go/event/broker.go
new file mode 100644
index 0000000..086043f
--- /dev/null
+++ b/examples/go/event/broker.go
@@ -0,0 +1,205 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+//
+// This is a simple AMQP broker implemented using the event-handler interface.
+//
+// It maintains a set of named in-memory queues of messages. Clients can send
+// messages to queues or subscribe to receive messages from them.
+//
+//
+
+package main
+
+import (
+	"container/list"
+	"flag"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"net"
+	"os"
+	"qpid.apache.org/proton"
+	"qpid.apache.org/proton/event"
+)
+
+// panicIf is simplistic error handling for example code, not recommended practice.
+func panicIf(err error) {
+	if err != nil {
+		panic(err)
+	}
+}
+
+// queue is a structure representing a queue.
+type queue struct {
+	name      string              // Name of queue
+	messages  *list.List          // List of event.Message
+	consumers map[event.Link]bool // Set of consumer links
+}
+
+func newQueue(name string) *queue {
+	debug.Printf("Create queue %s\n", name)
+	return &queue{name, list.New(), make(map[event.Link]bool)}
+}
+
+func (q *queue) subscribe(link event.Link) {
+	debug.Printf("Subscribe to %s\n", q.name)
+	q.consumers[link] = true
+}
+
+func (q *queue) unsubscribe(link event.Link) {
+	debug.Printf("Unsubscribe from %s\n", q.name)
+	delete(q.consumers, link)
+}
+
+func (q *queue) empty() bool {
+	return len(q.consumers) == 0 && q.messages.Len() == 0
+}
+
+func (q *queue) publish(message proton.Message) {
+	debug.Printf("Push to %s: %#v\n", q.name, message)
+	q.messages.PushBack(message)
+	q.dispatch()
+}
+
+func (q *queue) dispatchTo(link event.Link) bool {
+	if q.messages.Len() != 0 && link.Credit() > 0 {
+		message := q.messages.Front().Value.(proton.Message)
+		debug.Printf("Pop from %s: %#v\n", q.name, message)
+		// The first return parameter is an event.Delivery.
+		// The Deliver can be used to track message status, e.g. so we can re-delver on failure.
+		// This demo broker doesn't do that.
+		_, err := message.Send(link)
+		panicIf(err)
+		q.messages.Remove(q.messages.Front())
+		return true
+	}
+	return false
+}
+
+func (q *queue) dispatch() (dispatched bool) {
+	for c, _ := range q.consumers {
+		dispatched = dispatched || q.dispatchTo(c)
+	}
+	return
+}
+
+// broker implements event.MessagingHandler and reacts to events by moving messages on or off queues.
+type broker struct {
+	queues map[string]*queue
+	pumps  map[*event.Pump]struct{} // Set of running event pumps (i.e. connections)
+}
+
+func newBroker() *broker {
+	return &broker{queues: make(map[string]*queue), pumps: make(map[*event.Pump]struct{})}
+}
+
+func (b *broker) getQueue(name string) *queue {
+	q := b.queues[name]
+	if q == nil {
+		q = newQueue(name)
+		b.queues[name] = q
+	}
+	return q
+}
+
+func (b *broker) Handle(t event.MessagingEventType, e event.Event) error {
+	switch t {
+
+	case event.MLinkOpening:
+		if e.Link().IsSender() {
+			// FIXME aconway 2015-03-23: handle dynamic consumers
+			b.getQueue(e.Link().RemoteSource().Address()).subscribe(e.Link())
+		}
+
+	case event.MLinkClosing:
+		if e.Link().IsSender() {
+			q := b.getQueue(e.Link().RemoteSource().Address())
+			q.unsubscribe(e.Link())
+			if q.empty() {
+				delete(b.queues, q.name)
+			}
+		}
+
+	case event.MSendable:
+		b.getQueue(e.Link().RemoteSource().Address()).dispatchTo(e.Link())
+
+	case event.MMessage:
+		m, err := proton.EventMessage(e)
+		panicIf(err)
+		b.getQueue(e.Link().RemoteTarget().Address()).publish(m)
+	}
+	return nil
+}
+
+func (b *broker) listen(addr string) (err error) {
+	// Use the standard Go "net" package to listen for connections.
+	info.Printf("Listening on %s\n", addr)
+	listener, err := net.Listen("tcp", addr)
+	if err != nil {
+		return err
+	}
+	defer listener.Close()
+	for {
+		conn, err := listener.Accept()
+		if err != nil {
+			info.Printf("Accept error: %s\n", err)
+			continue
+		}
+		info.Printf("Accepted connection %s<-%s\n", conn.LocalAddr(), conn.RemoteAddr())
+		pump, err := event.NewPump(conn, event.NewMessagingDelegator(b))
+		panicIf(err)
+		pump.Server()
+		b.pumps[pump] = struct{}{}
+		go pump.Run()
+	}
+}
+
+var addr = flag.String("addr", ":amqp", "Listening address")
+var quiet = flag.Bool("quiet", false, "Don't print informational messages")
+var debugFlag = flag.Bool("debug", false, "Print debugging messages")
+var info, debug *log.Logger
+
+func output(enable bool) io.Writer {
+	if enable {
+		return os.Stdout
+	} else {
+		return ioutil.Discard
+	}
+}
+
+func main() {
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr, `
+Usage: %s [queue ...]
+A simple broker. Queues are created automatically for sender or receiver addrsses.
+`, os.Args[0])
+		flag.PrintDefaults()
+	}
+	flag.Parse()
+	debug = log.New(output(*debugFlag), "debug: ", log.Ltime)
+	info = log.New(output(!*quiet), "info: ", log.Ltime)
+	b := newBroker()
+	err := b.listen(*addr)
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/examples/go/example.go
----------------------------------------------------------------------
diff --git a/examples/go/example.go b/examples/go/example.go
new file mode 100644
index 0000000..08ecfeb
--- /dev/null
+++ b/examples/go/example.go
@@ -0,0 +1,54 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+package main
+
+import (
+	"fmt"
+	"qpid.apache.org/proton"
+	"sync"
+)
+
+func receive(c proton.Connection, addr string, wait *sync.WaitGroup) {
+	defer wait.Done()
+	r := c.Receiver(addr)
+	defer r.Close()
+	for m := range r.Receive { // r.Receive is a chan Message
+		fmt.Println("received: ", addr, m.Body(), m.Subject())
+		if m.Subject() == "stop" {
+			return
+		}
+	}
+}
+
+func main() {
+	var c1, c2 proton.Connection
+	c1.Open("amqp://foo:amqp")
+	defer c1.Close()
+	c2.Open("amqp://localhost:4567")
+	defer c2.Close()
+
+	var wait sync.WaitGroup
+	wait.Add(2)
+
+	go receive(c1, "foo", &wait)
+	go receive(c2, "bar", &wait)
+
+	wait.Wait()
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/proton-c/bindings/go/README.md
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/README.md b/proton-c/bindings/go/README.md
index c8c32b1..8de11ce 100644
--- a/proton-c/bindings/go/README.md
+++ b/proton-c/bindings/go/README.md
@@ -1,57 +1,158 @@
-# *EXPERIMENTAL* Go binding for proton
+y# *EXPERIMENTAL* Go binding for proton
 
 This is the beginning of a [Go](http://golang.org) binding for proton.
 
-This work is in very early *experimental* stages, *everything* might change in
-future.  Comments and contributions are strongly encouraged, this experiment is
-public so early feedback can guide development.
+This work is in early *experimental* stages, *everything* may change in future.
+Comments and contributions are strongly encouraged, this experiment is public so
+early feedback can guide development.
 
 - Email <proton@qpid.apache.org>
 - Create issues <https://issues.apache.org/jira/browse/PROTON>, attach patches to an issue.
 
 ## Goals
 
-The API will be inspired by the reactive, event-driven python API. Key features:
+The API should
 
+- be idiomatic, unsurprising, and easy to use for Go developers.
 - support client and server development.
-- incremental composition of functionality via handlers.
-- default handlers to make simple tasks simple.
-- deep access to AMQP protocol events when that is required.
-
-The API will be idiomatic, unsurprising, and easy to use for Go developers.
+- make simple tasks simple.
+- provide deep access to AMQP protocol when that is required.
 
 There are two types of developer we want to support
 
-1. For Go developers using AMQP as a message transport:
+1. Go developers using AMQP as a message transport:
    - Straightforward conversions between Go built-in types and AMQP types.
    - Easy message exchange via Go channels to support use in goroutines.
 
-2. For AMQP-aware developers using Go as an implementation language:
+2. AMQP-aware developers using Go as an implementation language:
    - Go types to exactly represent all AMQP types and encoding details.
-   - Full access to AMQP concepts like connections, sessions and links via handler interfaces.
+   - Full access to AMQP concepts like connections, sessions and links.
 
 We will follow conventions of the C and python API where possible to help
 cross-language developers but idiomatic Go is the overriding consideration.
 
-## API design thoughts
+## Status
+
+The current code is a foundation, not an implementation of the target API.
 
-The original idea was to provide a reactive API similar to the proton C and
-python APIs reavctive APIs. Looking more carefully at Go this does not seem like
-the right direction. Go provides built-in fine-grained concurrency and
-specifically aims to reduce the complexity traditional designs based on polling,
-callbacks and threading with communication between concurrent goroutines.
+There are two Go modules so far. See the documentation using
 
-So my current thinking is to build on the C reactor API as the internal core,
-but present message exchange to the user as messages on Go channels. There is a
-strong resemblence between Go channels and AMQP links that we can exploit.
+    godoc apache.org/proton
+    godoc apache.org/proton/event
 
-The reactor model probably still has a role for advanced users that want fine
-control over specifc AMPQ protocol features. The challenge will be to fit these
-models together.
+The event module contains a straightforward mapping of the proton event API and
+the simplified MessagingHandler python API.
 
-## Status
+The proton module contains the mapping between AMQP types and messages and Go
+types.
+
+## The event driven API
+
+The event module contains
+
+- Go Proton events (AMQP events only, no reactor events yet)
+- Go MessagingHandler events (equivalent to python MessagingHandler.)
+- Pump to feed data between a Go net.Conn connection and a proton event loop.
+
+The Pump uses 3 goroutines per connection, one to read, one to write and one to
+run the proton event loop. Proton's thread-unsafe data is never used outside the
+event loop goroutine.
+
+This API provides direct access to proton events, equivalent to C or python
+event API. It does not yet support reactor events or allow multiple connections
+to be handled in a single event loop goroutine, these are temporary limitations.
+
+There is one example: examples/go/broker.go. It is a port of
+examples/python/broker.py and can be used with the python `simple_send.py` and
+`simple_recv.py` clients.
+
+The broker example works for simple tests but is concurrency-unsafe. It uses a
+single `broker`, implementing MessagingHandler, with multiple pumps. The proton
+event loops are safe in their separate goroutines but the `broker` state (queues
+etc.) is not. We can fix this by funneling multiple connections into a single
+event loop as mentioned above.
+
+However this API is not the end of the story. It will be the foundation to build
+a more Go-like API that allows *any* goroutine to send or receive messages
+without having to know anything about event loops or pumps.
+
+## The Go API
+
+The goal: A procedural API that allows any user goroutine to send and receive
+AMQP messages and other information (acknowledgments, flow control instructions
+etc.) using channels. There will be no user-visible locks and no need to run
+user code in special goroutines, e.g. as handlers in a proton event loop.
+
+There is a (trivial, speculative, incomplete) example in examples/go/example.go
+of what part of it might look like. It shows receiving messages concurrently
+from two different connections in a single goroutine (it omits sessions). 
+
+There is a tempting analogy between Go channels and AMQP links, but Go channels
+are much simpler beasts than AMQP links. It is likely a link will be implemented
+by a Go type that uses more than one channel. E.g. there will probably be
+separate channels for messages and acknowledgments, perhaps also for flow
+control status.
+
+## Why a separate API for Go?
+
+Go is a concurrent language and encourages applications to be divided into
+concurrent *goroutines*. It provides traditional locking but it encourages the
+use *channels* to communicate between goroutines without explicit locks:
+
+  "Share memory by communicating, don't communicate by sharing memory"
+
+The idea is that a given value is only operated on by one goroutine at a time,
+but values can easily be passed from one goroutine to another. This removes much
+of the need for locking.
+
+Go literature distinguishes between:
+
+- *concurrency*: "keeping track of things that could be done in parallel"
+- *parallelism*: "actually doing things in parallel"
+
+The application expresses concurrency by starting goroutines for potentially
+concurrent tasks. The Go run-times schedule the activity of goroutines onto a
+small number (possibly one) of actual parallel executions.
+
+Even with *no* parallelism, concurrency lets the Go run-times *order* work with
+respect to events like file descriptors being readable/writable, channels having
+data, timers firing etc. Go automatically takes care of switching out goroutines
+that block or sleep so it is normal to write code in terms of blocking calls.
+
+Event-driven API (like poll, epoll, select or the proton event API) also
+channel unpredictably ordered events to actions in one or a small pool of
+execution threads. However this requires a different style of programming:
+"event-driven" or "reactive" programming. Go developers call it "inside-out"
+programming. In an event-driven architecture blocking is a big problem as it
+consumes a scarce thread of execution, so actions that take time to complete
+have to be re-structured in terms of future event delivery.
+
+The promise of Go is that you can express your application in concurrent,
+procedural terms with simple blocking calls and the Go run-times will turn it
+inside-out for you. Write as many goroutines as you want, and let Go interleave
+and schedule them efficiently.
+
+For example: the Go equivalent of listening for connections is a goroutine with
+a simple endless loop that calls a blocking Listen() function and starts a
+goroutine for each new connection. Each connection has its own goroutine that
+deals with just that connection till it closes.
+
+The benefit is that the variables and logic live closer together. Once you're in
+a goroutine, you have everything you need in local variables, and they are
+preserved across blocking calls. There's no need to store details in context
+objects that you have to look up when handling a later event to figure out how
+to continue where you left off.
+
+So a Go-like proton API does not force the users code to run in an event-loop
+goroutine. Instead user goroutines communicate with the event loop(s) via
+channels.  There's no need to funnel connections into one event loop, in fact it
+makes no sense.  Connections can be processed concurrently so they should be
+processed in separate goroutines and left to Go to schedule. User goroutines can
+have simple loops that block channels till messages are available, the user can
+start as many or as few such goroutines as they wish to implement concurrency as
+simple or as complex as they wish. For example blocking request-response
+vs. asynchronous flows of messages and acknowledgments.
 
-Marshal and unmarshal most of the AMQP types (TODO: described, array)
 
 ## Layout
 
@@ -88,63 +189,3 @@ If you are new to Go then these are a good place to start:
 
 Then look at the tools and library docs at <http://golang.org> as you need them.
 
-## Design Notes
-
-### C wrapping philosophy
-
-We use `cgo` to call proton C functions directly. `cgo` is simpler and more
-direct than Swig and integrated into the Go tools.
-
-Calling C directly from Go is so easy that we will avoid low-level 1-1 wrappers
-for proton objects and focus on easy to use Go types that live within Go's
-automatic memory management. 
-
-Programmers that need lower-level access than we provide can go direct to C, but
-of course we will aim to make that unnecessary in all but the most unusual cases.
-
-###  Other considerations
-
-Go's use of channels for synchronization present interesting opportunities. Go
-also supports traditional locking, so we could adopt locking strategies similar
-to our other bindings, but we should investigate the Go-like alternatives. There
-are analogies between Go channels and AMQP links that we will probably exploit.
-
-## Implementation status
-
-Working on API to marshal/unmarshal AMQP data into Go types.
-
-The API will follow the style of the standard libraries encoding/json and encoding/xml.
-
-## To be done:
-
-Easy unmarshaling into native Go types:
-
-- String-like AMQP types (symbol, binary, string) into Go string or []byte
-- Numeric AMQP types into any Go numeric (numeric conversion)
-- Any AMQP type into GO reflect.Value choosing the closest native Go type
-- AMQP map into Go struct if keys match struct field names and values match field types
-- AMQP maps into map[K]T if all AMQP keys/values can convert to K and T (reflect.Value allowed)
-- AMQP list into []T if all list elements can convert to T (reflect.Value allowed)
-- AMQP list into Go struct if AMQP types match struct field types in order.
-
-Easy marshaling of native Go types:
-
-- Go struct: amqp map with field names as string keys (use tags to customize?)
-- Go string to AMQP string, Go []byte to AMQP binary, Go numerics to closest AMQP numeric
-- Go []T to AMQP list
-- Go map to AMQP map
-
-Customization:
-
-- Standard encoding libraries define Marshaler and Unmarshaler interfaces.
-- User implements to customize behavior of a user type.
-- Does this require exposing the proton codec?
-
-Exact (strict) (un)marshaling:
-
-- Special Go AMQP types that exactly reflect AMQP types & encodings.
-- Unmarshal to special types only if exact match for wire
-- Marshal special types exactly
-- Define AMQPValue which can unmarshal from any AMQP type using strict unmarshaling types.
-
-Use of tags to control AMQP marshalling/unmarshalling?

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/proton-c/bindings/go/src/Makefile
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/Makefile b/proton-c/bindings/go/src/Makefile
new file mode 100644
index 0000000..bbcaded
--- /dev/null
+++ b/proton-c/bindings/go/src/Makefile
@@ -0,0 +1,25 @@
+# FIXME aconway 2015-04-09: integrate with cmake
+
+#GOFLAGS=-compiler gccgo -gccgoflags "-g -O0"
+#GOFLAGS=-gcflags "-N -l"
+
+GENERATED=qpid.apache.org/proton/event/wrappers_gen.go
+
+test: $(GENERATED)
+	go test $(GOFLAGS) qpid.apache.org/proton
+	go test $(GOFLAGS) qpid.apache.org/proton/event
+
+rebuild: $(GENERATED)
+	go build -a $(GOFLAGS) qpid.apache.org/proton
+	go build -a $(GOFLAGS) qpid.apache.org/proton/event
+	go test $(GOFLAGS) -c -a qpid.apache.org/proton
+	go test $(GOFLAGS) -c -a qpid.apache.org/proton/event
+
+qpid.apache.org/proton/event/wrappers_gen.go: genwrap.go ../../../include/proton/*.h
+	go run genwrap.go
+
+broker: test
+	go build $(GOFLAGS) ~/proton/examples/go/event/broker.go
+	./broker
+
+

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/proton-c/bindings/go/src/genwrap.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/genwrap.go b/proton-c/bindings/go/src/genwrap.go
new file mode 100644
index 0000000..83eb34c
--- /dev/null
+++ b/proton-c/bindings/go/src/genwrap.go
@@ -0,0 +1,398 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+// Code generator to generate a think Go wrapper API around the C proton API.
+//
+
+package main
+
+import (
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path"
+	"regexp"
+	"strings"
+	"text/template"
+)
+
+func mixedCase(s string) string {
+	result := ""
+	for _, w := range strings.Split(s, "_") {
+		if w != "" {
+			result = result + strings.ToUpper(w[0:1]) + strings.ToLower(w[1:])
+		}
+	}
+	return result
+}
+
+var templateFuncs = template.FuncMap{"mixedCase": mixedCase}
+
+func doTemplate(out io.Writer, data interface{}, tmpl string) {
+	panicIf(template.Must(template.New("").Funcs(templateFuncs).Parse(tmpl)).Execute(out, data))
+}
+
+type enumType struct {
+	Name   string
+	Values []string
+}
+
+// Find enums in a header file return map of enum name to values.
+func findEnums(header string) (enums []enumType) {
+	for _, enum := range enumDefRe.FindAllStringSubmatch(header, -1) {
+		enums = append(enums, enumType{enum[2], enumValRe.FindAllString(enum[1], -1)})
+	}
+	return enums
+}
+
+func genEnum(out io.Writer, name string, values []string) {
+	doTemplate(out, []interface{}{name, values}, `{{$enumName := index . 0}}{{$values := index . 1}}
+type {{mixedCase $enumName}} C.pn_{{$enumName}}_t
+const ({{range $values}}
+	{{mixedCase .}} {{mixedCase $enumName}} = C.{{.}} {{end}}
+)
+
+func (e {{mixedCase $enumName}}) String() string {
+	switch e {
+{{range $values}}
+	case C.{{.}}: return "{{mixedCase .}}" {{end}}
+	}
+	return "unknown"
+}
+`)
+}
+
+var (
+	reSpace = regexp.MustCompile("\\s+")
+)
+
+func panicIf(err error) {
+	if err != nil {
+		panic(err)
+	}
+}
+
+func readHeader(name string) string {
+	file, err := os.Open(path.Join("..", "..", "..", "include", "proton", name+".h"))
+	panicIf(err)
+	defer file.Close()
+	s, err := ioutil.ReadAll(file)
+	panicIf(err)
+	return string(s)
+}
+
+var copyright string = `/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+//
+// NOTE: This file was generated by genwrap.go, do not edit it by hand.
+//
+`
+
+type eventType struct {
+	// C, function and interface names for the event
+	Name, Cname, Fname, Iname string
+}
+
+func newEventType(cName string) eventType {
+	var etype eventType
+	etype.Cname = cName
+	etype.Name = mixedCase(strings.TrimPrefix(cName, "PN_"))
+	etype.Fname = "On" + etype.Name
+	etype.Iname = etype.Fname + "Interface"
+	return etype
+}
+
+var (
+	enumDefRe   = regexp.MustCompile("typedef enum {([^}]*)} pn_([a-z_]+)_t;")
+	enumValRe   = regexp.MustCompile("PN_[A-Z_]+")
+	skipEventRe = regexp.MustCompile("EVENT_NONE|REACTOR|SELECTABLE|TIMER")
+	skipFnRe    = regexp.MustCompile("attach|context|class|collect|^recv$|^send$")
+)
+
+// Generate event wrappers.
+func event(out io.Writer) {
+	event_h := readHeader("event")
+
+	// event.h API functions
+	apiWrapFns("event", event_h, out)
+	fmt.Fprintln(out, `func (e Event) String() string { return e.Type().String() }`)
+
+	// Get all the pn_event_type_t enum values
+	var etypes []eventType
+	enums := findEnums(event_h)
+	for _, e := range enums[0].Values {
+		if skipEventRe.FindStringSubmatch(e) == nil {
+			etypes = append(etypes, newEventType(e))
+		}
+	}
+
+	doTemplate(out, etypes, `
+type EventType int
+const ({{range .}}
+	 E{{.Name}} EventType = C.{{.Cname}}{{end}}
+)
+`)
+
+	doTemplate(out, etypes, `
+func (e EventType) String() string {
+	switch e {
+{{range .}}
+	case C.{{.Cname}}: return "{{.Name}}"{{end}}
+	}
+	return "Unknown"
+}
+`)
+}
+
+type genType struct {
+	Ctype, Gotype string
+	ToGo          func(value string) string
+	ToC           func(value string) string
+}
+
+func (g genType) printBody(out io.Writer, value string) {
+	if g.Gotype != "" {
+		fmt.Fprintf(out, "return %s", g.ToGo(value))
+	} else {
+		fmt.Fprintf(out, "%s", value)
+	}
+}
+
+func (g genType) goLiteral(value string) string {
+	return fmt.Sprintf("%s{%s}", g.Gotype, value)
+}
+
+func (g genType) goConvert(value string) string {
+	switch g.Gotype {
+	case "string":
+		return fmt.Sprintf("C.GoString(%s)", value)
+	default:
+		return fmt.Sprintf("%s(%s)", g.Gotype, value)
+	}
+}
+
+var notStruct = map[string]bool{
+	"EventType":        true,
+	"SndSettleMode":    true,
+	"RcvSettleMode":    true,
+	"TerminusType":     true,
+	"State":            true,
+	"Durability":       true,
+	"ExpiryPolicy":     true,
+	"DistributionMode": true,
+}
+
+func mapType(ctype string) (g genType) {
+	g.Ctype = "C." + strings.Trim(ctype, " \n")
+
+	switch g.Ctype {
+	case "C.void":
+		g.Gotype = ""
+	case "C.size_t":
+		g.Gotype = "uint"
+	case "C.int":
+		g.Gotype = "int"
+	case "C.void *":
+		g.Gotype = "unsafe.Pointer"
+		g.Ctype = "unsafe.Pointer"
+	case "C.bool":
+		g.Gotype = "bool"
+	case "C.ssize_t":
+		g.Gotype = "int"
+	case "C.uint64_t":
+		g.Gotype = "uint64"
+	case "C.uint32_t":
+		g.Gotype = "uint32"
+	case "C.const char *":
+		g.Gotype = "string"
+		g.Ctype = "C.CString"
+	case "C.char *":
+		g.Gotype = "string"
+		g.Ctype = "C.CString"
+	case "C.pn_seconds_t":
+		g.Gotype = "time.Duration"
+		g.ToGo = func(v string) string { return fmt.Sprintf("(time.Duration(%s) * time.Second)", v) }
+	case "C.pn_error_t *":
+		g.Gotype = "error"
+		g.ToGo = func(v string) string { return fmt.Sprintf("pnError(%s)", v) }
+	default:
+		pnId := regexp.MustCompile(" *pn_([a-z_]+)_t *\\*? *")
+		match := pnId.FindStringSubmatch(g.Ctype)
+		if match == nil {
+			panic(fmt.Errorf("unknown C type %#v", g.Ctype))
+		}
+		g.Gotype = mixedCase(match[1])
+		if !notStruct[g.Gotype] {
+			g.ToGo = g.goLiteral
+			g.ToC = func(v string) string { return v + ".pn" }
+		}
+	}
+	if g.ToGo == nil {
+		g.ToGo = g.goConvert // Use conversion by default.
+	}
+	if g.ToC == nil {
+		g.ToC = func(v string) string { return fmt.Sprintf("%s(%s)", g.Ctype, v) }
+	}
+	return
+}
+
+type genArg struct {
+	Name string
+	genType
+}
+
+var typeNameRe = regexp.MustCompile("^(.*( |\\*))([^ *]+)$")
+
+func splitArgs(argstr string) []genArg {
+	argstr = strings.Trim(argstr, " \n")
+	if argstr == "" {
+		return []genArg{}
+	}
+	args := make([]genArg, 0)
+	for _, item := range strings.Split(argstr, ",") {
+		typeName := typeNameRe.FindStringSubmatch(item)
+		if typeName == nil {
+			panic(fmt.Errorf("Can't split argument type/name %#v", item))
+		}
+		cType := strings.Trim(typeName[1], " \n")
+		name := strings.Trim(typeName[3], " \n")
+		if name == "type" {
+			name = "type_"
+		}
+		args = append(args, genArg{name, mapType(cType)})
+	}
+	return args
+}
+
+func goArgs(args []genArg) string {
+	l := ""
+	for i, arg := range args {
+		if i != 0 {
+			l += ", "
+		}
+		l += arg.Name + " " + arg.Gotype
+	}
+	return l
+}
+
+func cArgs(args []genArg) string {
+	l := ""
+	for _, arg := range args {
+		l += fmt.Sprintf(", %s", arg.ToC(arg.Name))
+	}
+	return l
+}
+
+// Return the go name of the function or "" to skip the function.
+func goFnName(api, fname string) string {
+	// Skip class, context and attachment functions.
+	if skipFnRe.FindStringSubmatch(fname) != nil {
+		return ""
+	}
+	switch api + "." + fname {
+	case "link.get_drain":
+		return "IsDrain"
+	default:
+		return mixedCase(strings.TrimPrefix(fname, "get_"))
+	}
+}
+
+func apiWrapFns(api, header string, out io.Writer) {
+	fmt.Fprintf(out, "type %s struct{pn *C.pn_%s_t}\n", mixedCase(api), api)
+	fmt.Fprintf(out, "func (%c %s) isNil() bool { return %c.pn == nil }\n", api[0], mixedCase(api), api[0])
+	fn := regexp.MustCompile(fmt.Sprintf(`PN_EXTERN ([a-z0-9_ ]+ *\*?) *pn_%s_([a-z_]+)\(pn_%s_t *\*[a-z_]+ *,? *([^)]*)\)`, api, api))
+	for _, m := range fn.FindAllStringSubmatch(header, -1) {
+		rtype, fname, argstr := mapType(m[1]), m[2], m[3]
+		gname := goFnName(api, fname)
+		if gname == "" { // Skip
+			continue
+		}
+		args := splitArgs(argstr)
+		fmt.Fprintf(out, "func (%c %s) %s", api[0], mixedCase(api), gname)
+		fmt.Fprintf(out, "(%s) %s { ", goArgs(args), rtype.Gotype)
+		rtype.printBody(out, fmt.Sprintf("C.pn_%s_%s(%c.pn%s)", api, fname, api[0], cArgs(args)))
+		fmt.Fprintf(out, "}\n")
+	}
+}
+
+func main() {
+	outpath := path.Join("qpid.apache.org", "proton", "event", "wrappers_gen.go")
+	out, err := os.Create(outpath)
+	panicIf(err)
+	defer out.Close()
+
+	apis := []string{"session", "link", "delivery", "disposition", "condition", "terminus", "connection"}
+	fmt.Fprintln(out, copyright)
+	fmt.Fprint(out, `
+package event
+
+import (
+	"time"
+)
+
+// #include <proton/types.h>
+// #include <proton/event.h>
+`)
+	for _, api := range apis {
+		fmt.Fprintf(out, "// #include <proton/%s.h>\n", api)
+	}
+	fmt.Fprintln(out, `import "C"`)
+
+	event(out)
+
+	for _, api := range apis {
+		fmt.Fprintf(out, "// Wrappers for declarations in %s.h\n\n", api)
+		header := readHeader(api)
+		enums := findEnums(header)
+		for _, e := range enums {
+			genEnum(out, e.Name, e.Values)
+		}
+		apiWrapFns(api, header, out)
+	}
+	out.Close()
+
+	// Run gofmt.
+	cmd := exec.Command("gofmt", "-w", outpath)
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	err = cmd.Run()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "gofmt: %s", err)
+		os.Exit(1)
+	}
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/proton-c/bindings/go/src/qpid.apache.org/proton/doc.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/doc.go b/proton-c/bindings/go/src/qpid.apache.org/proton/doc.go
index 34f85fe..40e1f7c 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/proton/doc.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/doc.go
@@ -30,4 +30,7 @@ described in the documentation of the Marshal and Unmarshal functions.
 */
 package proton
 
+// #cgo LDFLAGS: -lqpid-proton
+import "C"
+
 // This file is just for the package comment.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/proton-c/bindings/go/src/qpid.apache.org/proton/dummy.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/dummy.go b/proton-c/bindings/go/src/qpid.apache.org/proton/dummy.go
new file mode 100644
index 0000000..823fb7f
--- /dev/null
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/dummy.go
@@ -0,0 +1,45 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+package proton
+
+type Connection struct{}
+
+func (c Connection) Open(string) {}
+func (c Connection) Close()      {}
+func (c Connection) Receiver(addr string) *Receiver {
+	r := &Receiver{make(chan Message)}
+	// FIXME aconway 2015-04-10: dummy implementation to test initial example, will be removed.
+	go func() {
+		m := NewMessage()
+		m.SetBody(addr)
+		r.Receive <- m
+		m = NewMessage()
+		m.SetBody(addr)
+		m.SetSubject("stop")
+		r.Receive <- m
+	}()
+	return r
+}
+
+type Receiver struct {
+	Receive chan Message
+}
+
+func (r Receiver) Close() {}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/proton-c/bindings/go/src/qpid.apache.org/proton/error.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/error.go b/proton-c/bindings/go/src/qpid.apache.org/proton/error.go
index f9cc948..95927bc 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/proton/error.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/error.go
@@ -19,6 +19,8 @@ under the License.
 
 package proton
 
+// FIXME aconway 2015-04-08: consolidate with event/errors.go
+
 // #include <proton/error.h>
 // #include <proton/codec.h>
 import "C"
@@ -46,24 +48,34 @@ func pnErrorName(code int) string {
 	if name != "" {
 		return name
 	} else {
-		return "unknown error code"
+		return fmt.Sprintf("unknown-error(%s)", code)
+	}
+}
+
+// pnError converst a pn_error_t to a Go error. Returns nil if e has 0 error status
+func pnError(prefix string, e *C.pn_error_t) error {
+	if e == nil || int(C.pn_error_code(e)) == 0 {
+		return nil
 	}
+	code := int(C.pn_error_code(e))
+	return errorf("%s %s: %s", pnErrorName(code), prefix,
+		C.GoString(C.pn_error_text(e)))
 }
 
 type BadUnmarshal struct {
-	AMQPType C.pn_type_t
+	AMQPType string
 	GoType   reflect.Type
 }
 
 func newBadUnmarshal(pnType C.pn_type_t, v interface{}) *BadUnmarshal {
-	return &BadUnmarshal{pnType, reflect.TypeOf(v)}
+	return &BadUnmarshal{pnTypeString(pnType), reflect.TypeOf(v)}
 }
 
 func (e BadUnmarshal) Error() string {
 	if e.GoType.Kind() != reflect.Ptr {
 		return fmt.Sprintf("proton: cannot unmarshal to type %s, not a pointer", e.GoType)
 	} else {
-		return fmt.Sprintf("proton: cannot unmarshal AMQP %s to %s", getPnType(e.AMQPType), e.GoType)
+		return fmt.Sprintf("proton: cannot unmarshal AMQP %s to %s", e.AMQPType, e.GoType)
 	}
 }
 
@@ -72,14 +84,6 @@ func errorf(format string, a ...interface{}) error {
 	return fmt.Errorf("proton: %s", fmt.Sprintf(format, a...))
 }
 
-func pnDataError(data *C.pn_data_t) string {
-	err := C.pn_data_error(data)
-	if err != nil && int(C.pn_error_code(err)) != 0 {
-		return C.GoString(C.pn_error_text(err))
-	}
-	return ""
-}
-
 // doRecover is called to recover from internal panics
 func doRecover(err *error) {
 	r := recover()
@@ -94,3 +98,14 @@ func doRecover(err *error) {
 		panic(r)
 	}
 }
+
+// panicIf panics if condition is true, the panic value is errorf(fmt, args...)
+func panicIf(condition bool, fmt string, args ...interface{}) {
+	if condition {
+		panic(errorf(fmt, args...))
+	}
+}
+
+func dataError(prefix string, data *C.pn_data_t) error {
+	return pnError(prefix, C.pn_data_error(data))
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/proton-c/bindings/go/src/qpid.apache.org/proton/event/doc.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/event/doc.go b/proton-c/bindings/go/src/qpid.apache.org/proton/event/doc.go
new file mode 100644
index 0000000..d55dc7d
--- /dev/null
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/event/doc.go
@@ -0,0 +1,28 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+/*
+Package event provides an event-oriented API to the  proton AMQP engine.
+*/
+package event
+
+// #cgo LDFLAGS: -lqpid-proton
+import "C"
+
+// This file is just for the package comment.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/proton-c/bindings/go/src/qpid.apache.org/proton/event/error.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/event/error.go b/proton-c/bindings/go/src/qpid.apache.org/proton/event/error.go
new file mode 100644
index 0000000..42382a4
--- /dev/null
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/event/error.go
@@ -0,0 +1,77 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+package event // FIXME aconway 2015-03-26: duplicated from package proton, clean up
+
+// #include <proton/error.h>
+// #include <proton/codec.h>
+import "C"
+
+import (
+	"fmt"
+	"runtime"
+)
+
+var pnErrorNames = map[int]string{
+	C.PN_EOS:        "end of data",
+	C.PN_ERR:        "error",
+	C.PN_OVERFLOW:   "overflow",
+	C.PN_UNDERFLOW:  "underflow",
+	C.PN_STATE_ERR:  "bad state",
+	C.PN_ARG_ERR:    "invalid argument",
+	C.PN_TIMEOUT:    "timeout",
+	C.PN_INTR:       "interrupted",
+	C.PN_INPROGRESS: "in progress",
+}
+
+func pnErrorName(code int) string {
+	name := pnErrorNames[code]
+	if name != "" {
+		return name
+	} else {
+		return "unknown"
+	}
+}
+
+func pnError(e *C.pn_error_t) error {
+	if e == nil || C.pn_error_code(e) == 0 {
+		return nil
+	}
+	return errorf("%s: %s", pnErrorName(int(C.pn_error_code(e))), C.GoString(C.pn_error_text(e)))
+}
+
+// errorf creates an error with a formatted message
+func errorf(format string, a ...interface{}) error {
+	return fmt.Errorf("proton: %s", fmt.Sprintf(format, a...))
+}
+
+// doRecover is called to recover from internal panics
+func doRecover(err *error) {
+	r := recover()
+	switch r := r.(type) {
+	case nil:
+		return
+	case runtime.Error:
+		panic(r)
+	case error:
+		*err = r
+	default:
+		panic(r)
+	}
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/proton-c/bindings/go/src/qpid.apache.org/proton/event/handlers.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/event/handlers.go b/proton-c/bindings/go/src/qpid.apache.org/proton/event/handlers.go
new file mode 100644
index 0000000..d1ce953
--- /dev/null
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/event/handlers.go
@@ -0,0 +1,328 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+package event
+
+// #include <proton/reactor.h>
+// #include <proton/handlers.h>
+import "C"
+
+// CoreHandler handles core proton events.
+type CoreHandler interface {
+	// Handle is called with an event.
+	// Typically Handle() is implemented as a switch on e.Type()
+	Handle(e Event) error
+}
+
+// cHandler wraps a C pn_handler_t
+type cHandler struct {
+	pn *C.pn_handler_t
+}
+
+func (h cHandler) Handle(e Event) error {
+	C.pn_handler_dispatch(h.pn, e.pn, C.pn_event_type(e.pn))
+	return nil // FIXME aconway 2015-03-31: error handling
+}
+
+func HandShaker() CoreHandler {
+	return cHandler{C.pn_handshaker()}
+}
+
+func FlowController(prefetch int) CoreHandler {
+	return cHandler{C.pn_flowcontroller(C.int(prefetch))}
+}
+
+// MessagingHandler provides a higher-level, easier-to-use interface for writing
+// applications that send and receive messages.
+//
+// You can implement this interface and wrap it with a MessagingHandlerDelegator
+type MessagingHandler interface {
+	Handle(MessagingEventType, Event) error
+}
+
+// MessagingEventType provides an easier set of event types to work with
+// that the core proton EventType.
+//
+type MessagingEventType int
+
+const (
+	// The event loop starts.
+	MStart MessagingEventType = iota
+
+	// The peer closes the connection with an error condition.
+	MConnectionError
+
+	// The peer closes the session with an error condition.
+	MSessionError
+
+	// The peer closes the link with an error condition.
+	MLinkError
+
+	// The peer Initiates the opening of the connection.
+	MConnectionOpening
+
+	// The peer initiates the opening of the session.
+	MSessionOpening
+
+	// The peer initiates the opening of the link.
+	MLinkOpening
+
+	// The connection is opened.
+	MConnectionOpened
+
+	// The session is opened.
+	MSessionOpened
+
+	// The link is opened.
+	MLinkOpened
+
+	// The peer initiates the closing of the connection.
+	MConnectionClosing
+
+	// The peer initiates the closing of the session.
+	MSessionClosing
+
+	// The peer initiates the closing of the link.
+	MLinkClosing
+
+	// The connection is closed.
+	MConnectionClosed
+
+	// The session is closed.
+	MSessionClosed
+
+	// The link is closed.
+	MLinkClosed
+
+	// The socket is disconnected.
+	MDisconnected
+
+	// The sender link has credit and messages can
+	// therefore be transferred.
+	MSendable
+
+	// The remote peer accepts an outgoing message.
+	MAccepted
+
+	// The remote peer rejects an outgoing message.
+	MRejected
+
+	// The peer releases an outgoing message. Note that this may be in response to
+	// either the RELEASE or MODIFIED state as defined by the AMQP specification.
+	MReleased
+
+	// The peer has settled the outgoing message. This is the point at which it
+	// shouod never be retransmitted.
+	MSettled
+
+	// A message is received. Call proton.EventMessage(Event) to get the message.
+	// To manage the outcome of this messages (e.g. to accept or reject the message)
+	// use Event.Delivery().
+	MMessage
+)
+
+// Capture common patterns for endpoints opening/closing
+type endpointDelegator struct {
+	remoteOpen, remoteClose, localOpen, localClose EventType
+	opening, opened, closing, closed, error        MessagingEventType
+	endpoint                                       func(Event) Endpoint
+	delegate                                       MessagingHandler
+}
+
+func (d endpointDelegator) Handle(e Event) error {
+	endpoint := d.endpoint(e)
+	state := endpoint.State()
+
+	switch e.Type() {
+
+	case d.localOpen:
+		if state.RemoteOpen() {
+			return d.delegate.Handle(d.opened, e)
+		}
+
+	case d.remoteOpen:
+		switch {
+		case state.LocalOpen():
+			return d.delegate.Handle(d.opened, e)
+		case state.LocalUninitialized():
+			err := d.delegate.Handle(d.opening, e)
+			if err == nil {
+				endpoint.Open()
+			}
+			return err
+		}
+
+	case d.remoteClose:
+		switch {
+		case endpoint.RemoteCondition().IsSet():
+			d.delegate.Handle(d.error, e)
+		case state.LocalClosed():
+			d.delegate.Handle(d.closed, e)
+		default:
+			d.delegate.Handle(d.closing, e)
+		}
+		endpoint.Close()
+
+	case d.localClose:
+		// Nothing to do
+
+	default:
+		panic("internal error") // We shouldn't be called with any other event type.
+	}
+	return nil
+}
+
+// MessagingDelegator implments a CoreHandler and delegates to a MessagingHandler.
+// You can modify the exported fields before you pass the MessagingDelegator to
+// a Pump
+type MessagingDelegator struct {
+	delegate                   MessagingHandler
+	connection, session, link  endpointDelegator
+	handshaker, flowcontroller CoreHandler
+
+	// AutoSettle (default true) automatically pre-settle outgoing messages.
+	AutoSettle bool
+	// AutoAccept (default true) automatically accept and settle incoming messages
+	// if they are not settled by the delegate.
+	AutoAccept bool
+	// Prefetch (default 10) initial credit to issue for incoming links.
+	Prefetch int
+	// PeerCloseIsError (default false) if true a close by the peer will be treated as an error.
+	PeerCloseError bool
+}
+
+func NewMessagingDelegator(h MessagingHandler) CoreHandler {
+	return &MessagingDelegator{
+		delegate: h,
+		connection: endpointDelegator{
+			EConnectionRemoteOpen, EConnectionRemoteClose, EConnectionLocalOpen, EConnectionLocalClose,
+			MConnectionOpening, MConnectionOpened, MConnectionClosing, MConnectionClosed,
+			MConnectionError,
+			func(e Event) Endpoint { return e.Connection() },
+			h,
+		},
+		session: endpointDelegator{
+			ESessionRemoteOpen, ESessionRemoteClose, ESessionLocalOpen, ESessionLocalClose,
+			MSessionOpening, MSessionOpened, MSessionClosing, MSessionClosed,
+			MSessionError,
+			func(e Event) Endpoint { return e.Session() },
+			h,
+		},
+		link: endpointDelegator{
+			ELinkRemoteOpen, ELinkRemoteClose, ELinkLocalOpen, ELinkLocalClose,
+			MLinkOpening, MLinkOpened, MLinkClosing, MLinkClosed,
+			MLinkError,
+			func(e Event) Endpoint { return e.Link() },
+			h,
+		},
+		handshaker:     HandShaker(),
+		flowcontroller: nil,
+		AutoSettle:     true,
+		AutoAccept:     true,
+		Prefetch:       10,
+		PeerCloseError: false,
+	}
+}
+
+func handleIf(h CoreHandler, e Event) error {
+	if h != nil {
+		return h.Handle(e)
+	}
+	return nil
+}
+
+func (d *MessagingDelegator) Handle(e Event) error {
+	handleIf(d.handshaker, e)
+	handleIf(d.flowcontroller, e) // FIXME aconway 2015-03-31: error handling.
+
+	switch e.Type() {
+
+	case EConnectionInit:
+		d.flowcontroller = FlowController(d.Prefetch)
+		d.delegate.Handle(MStart, e)
+
+	case EConnectionRemoteOpen, EConnectionRemoteClose, EConnectionLocalOpen, EConnectionLocalClose:
+		return d.connection.Handle(e)
+
+	case ESessionRemoteOpen, ESessionRemoteClose, ESessionLocalOpen, ESessionLocalClose:
+		return d.session.Handle(e)
+
+	case ELinkRemoteOpen, ELinkRemoteClose, ELinkLocalOpen, ELinkLocalClose:
+		return d.link.Handle(e)
+
+	case ELinkFlow:
+		if e.Link().IsSender() && e.Link().Credit() > 0 {
+			return d.delegate.Handle(MSendable, e)
+		}
+
+	case EDelivery:
+		if e.Delivery().Link().IsReceiver() {
+			d.incoming(e)
+		} else {
+			d.outgoing(e)
+		}
+	}
+	return nil
+}
+
+func (d *MessagingDelegator) incoming(e Event) (err error) {
+	delivery := e.Delivery()
+	if delivery.Readable() && !delivery.Partial() {
+		if e.Link().State().LocalClosed() {
+			e.Link().Advance()
+			if d.AutoAccept {
+				delivery.Release(false)
+			}
+		} else {
+			err = d.delegate.Handle(MMessage, e)
+			e.Link().Advance()
+			if d.AutoAccept && !delivery.Settled() {
+				if err == nil {
+					delivery.Accept()
+				} else {
+					delivery.Reject()
+				}
+			}
+		}
+	} else if delivery.Updated() && delivery.Settled() {
+		err = d.delegate.Handle(MSettled, e)
+	}
+	return
+}
+
+func (d *MessagingDelegator) outgoing(e Event) (err error) {
+	delivery := e.Delivery()
+	if delivery.Updated() {
+		switch delivery.Remote().Type() {
+		case Accepted:
+			err = d.delegate.Handle(MAccepted, e)
+		case Rejected:
+			err = d.delegate.Handle(MRejected, e)
+		case Released, Modified:
+			err = d.delegate.Handle(MReleased, e)
+		}
+		if err == nil && delivery.Settled() {
+			err = d.delegate.Handle(MSettled, e)
+		}
+		if err == nil && d.AutoSettle {
+			delivery.Settle()
+		}
+	}
+	return
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/proton-c/bindings/go/src/qpid.apache.org/proton/event/pump.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/event/pump.go b/proton-c/bindings/go/src/qpid.apache.org/proton/event/pump.go
new file mode 100644
index 0000000..17257b5
--- /dev/null
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/event/pump.go
@@ -0,0 +1,262 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+package event
+
+// #include <proton/connection.h>
+// #include <proton/transport.h>
+// #include <proton/event.h>
+// #include <proton/reactor.h>
+// #include <proton/handlers.h>
+// #include <proton/transport.h>
+// #include <memory.h>
+//
+import "C"
+
+import (
+	"net"
+	"sync"
+	"unsafe"
+)
+
+// FIXME aconway 2015-04-09: We should allow data from multiple connections to be pumped
+// into a single event loop (using the proton Reactor)
+// That would allow the user to decide if they want an event-loop goroutine per connection
+// or if they want to handle several connections in one event loop.
+
+// Pump reads from a net.Conn, decodes AMQP events and calls the appropriate
+// Handler functions. Actions taken by Handler functions (such as sending messages)
+// are encoded and written to the net.Conn.
+//
+type Pump struct {
+	conn       net.Conn
+	transport  *C.pn_transport_t
+	connection *C.pn_connection_t
+	collector  *C.pn_collector_t
+	read       chan []byte    // Read buffers channel. Will close when pump closes.
+	write      chan []byte    // Write buffer channel. Must be closed when read closes.
+	waiter     sync.WaitGroup // Wait for read and write goroutines to complete.
+	handlers   []CoreHandler  // Handlers for proton events.
+
+	inject chan func()   // Functions inject into the loop
+	closed chan struct{} // This channel will be closed when the remote end closes.
+}
+
+const bufferSize = 4096
+
+// NewPump initializes a pump with a connection and handlers.. Call `go Run()` to start it running.
+func NewPump(conn net.Conn, handlers ...CoreHandler) (*Pump, error) {
+	p := &Pump{
+		conn:       conn,
+		transport:  C.pn_transport(),
+		connection: C.pn_connection(),
+		collector:  C.pn_collector(),
+		handlers:   handlers,
+		read:       make(chan []byte),
+		write:      make(chan []byte),
+		inject:     make(chan func()),
+		closed:     make(chan struct{}),
+	}
+	p.handlers = append(p.handlers, handlers...)
+
+	if p.transport == nil || p.connection == nil || p.collector == nil {
+		return nil, errorf("failed to allocate pump")
+	}
+	pnErr := int(C.pn_transport_bind(p.transport, p.connection))
+	if pnErr != 0 {
+		return nil, errorf("cannot setup pump: %s", pnErrorName(pnErr))
+	}
+	C.pn_connection_collect(p.connection, p.collector)
+	C.pn_connection_open(p.connection)
+	return p, nil
+}
+
+// Server puts the Pump in server mode, meaning it will auto-detect security settings on
+// the incoming connnection such as use of SASL and SSL.
+// Must be called before Run()
+func (p *Pump) Server() {
+	C.pn_transport_set_server(p.transport)
+}
+
+// Run the pump. Normally called in a goroutine as: go pump.Run()
+func (p *Pump) Run() {
+	go p.run()
+}
+
+// Pump handles the connction close event to close itself.
+func (p *Pump) Handle(e Event) error {
+	switch e.Type() {
+	case EConnectionLocalClose:
+		return p.close()
+	}
+	return nil
+}
+
+// Closing the pump will also close the net.Conn and stop associated goroutines.
+func (p *Pump) Close() error {
+	// FIXME aconway 2015-04-08: broken
+
+	// Note this is called externally, outside the proton event loop.
+	// Polite AMQP close
+	p.inject <- func() { C.pn_connection_close(p.connection) }
+	_, _ = <-p.closed // Wait for remote close
+	return p.close()
+}
+
+// close private implementation, call in the event loop.
+func (p *Pump) close() error {
+	p.conn.Close()
+	p.waiter.Wait()
+	if p.connection != nil {
+		C.pn_connection_free(p.connection)
+	}
+	if p.transport != nil {
+		C.pn_transport_free(p.transport)
+	}
+	if p.collector != nil {
+		C.pn_collector_free(p.collector)
+	}
+	for _, h := range p.handlers {
+		switch h := h.(type) {
+		case cHandler:
+			C.pn_handler_free(h.pn)
+		}
+	}
+	return nil // FIXME aconway 2015-03-31: error handling
+}
+
+// Start goroutines to feed the pn_transport_t from the pump.
+func (c *Pump) run() error {
+	// FIXME aconway 2015-03-17: error handling
+	c.waiter.Add(2)
+	var readError, writeError error
+
+	go func() { // Read
+		rbuf, rbuf2 := make([]byte, bufferSize), make([]byte, bufferSize)
+		for {
+			rbuf = rbuf[:cap(rbuf)]
+			n, err := c.conn.Read(rbuf)
+			if n > 0 {
+				c.read <- rbuf[:n]
+			}
+			if err != nil {
+				readError = err
+				break
+			}
+			rbuf, rbuf2 = rbuf2, rbuf // Swap the buffers, fill the one not in use.
+		}
+		close(c.read)
+		c.waiter.Done()
+	}()
+
+	go func() { // Write
+		for wbuf := range c.write {
+			_, err := c.conn.Write(wbuf)
+			if err != nil {
+				writeError = err
+				break
+			}
+		}
+		c.waiter.Done()
+	}()
+
+	// Proton driver loop
+	wbuf, wbuf2 := make([]byte, bufferSize), make([]byte, bufferSize)
+	wbuf = c.pop(wbuf) // First write buffer
+	for {              // handle pn_transport_t
+		select {
+		case buf, ok := <-c.read: // Read a buffer
+			if !ok { // Read channel closed
+				break
+			}
+			c.push(buf)
+
+		case c.write <- wbuf: // Write a buffer
+			wbuf, wbuf2 = wbuf2, wbuf // Swap the buffers, fill the unused one.
+			wbuf = c.pop(wbuf)        // Next buffer to write
+
+		case f := <-c.inject: // Function injected from another goroutine
+			f()
+		}
+		c.process() // FIXME aconway 2015-03-17: error handling
+	}
+
+	close(c.write)
+	c.waiter.Wait() // Wait for read/write goroutines to finish
+	switch {
+	case readError != nil:
+		return readError
+	case writeError != nil:
+		return writeError
+	}
+	return nil
+}
+
+func minInt(a, b int) int {
+	if a < b {
+		return a
+	} else {
+		return b
+	}
+}
+
+func (c *Pump) pop(buf []byte) []byte {
+	pending := int(C.pn_transport_pending(c.transport))
+	if pending == int(C.PN_EOS) {
+		return nil
+	}
+	if pending < 0 {
+		panic(errorf(pnErrorName(pending)))
+	}
+	size := minInt(pending, cap(buf))
+	buf = buf[:size]
+	if size == 0 {
+		return buf
+	}
+	C.memcpy(unsafe.Pointer(&buf[0]), unsafe.Pointer(C.pn_transport_head(c.transport)), C.size_t(size))
+	C.pn_transport_pop(c.transport, C.size_t(size))
+	return buf
+}
+
+func (c *Pump) push(buf []byte) {
+	buf2 := buf
+	for len(buf2) > 0 {
+		n := int(C.pn_transport_push(c.transport, (*C.char)(unsafe.Pointer((&buf2[0]))), C.size_t(len(buf2))))
+		if n <= 0 {
+			panic(errorf("error in transport: %s", pnErrorName(n)))
+		}
+		buf2 = buf2[n:]
+	}
+}
+
+func (c *Pump) process() {
+	for ce := C.pn_collector_peek(c.collector); ce != nil; ce = C.pn_collector_peek(c.collector) {
+		e := Event{ce}
+		for _, h := range c.handlers {
+			h.Handle(e) // FIXME aconway 2015-03-18: error handling
+		}
+		C.pn_collector_pop(c.collector)
+	}
+}
+
+func (c *Pump) AddHandlers(handlers ...CoreHandler) {
+	c.inject <- func() {
+		c.handlers = append(c.handlers, handlers...)
+	}
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/proton-c/bindings/go/src/qpid.apache.org/proton/event/wrappers.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/event/wrappers.go b/proton-c/bindings/go/src/qpid.apache.org/proton/event/wrappers.go
new file mode 100644
index 0000000..c3c0a7d
--- /dev/null
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/event/wrappers.go
@@ -0,0 +1,137 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+package event
+
+//#include <proton/codec.h>
+//#include <proton/connection.h>
+//#include <proton/delivery.h>
+//#include <proton/event.h>
+//#include <proton/transport.h>
+//#include <proton/link.h>
+import "C"
+
+import (
+	"unsafe"
+)
+
+// Data holds a pointer to decoded AMQP data, proton.marshal/unmarshal to access it as Go data.
+type Data struct{ pn *C.pn_data_t }
+
+func NewData(p unsafe.Pointer) Data { return Data{(*C.pn_data_t)(p)} }
+
+func (d Data) Free()                   { C.pn_data_free(d.pn) }
+func (d Data) Pointer() unsafe.Pointer { return unsafe.Pointer(d.pn) }
+func (d Data) Clear()                  { C.pn_data_clear(d.pn) }
+func (d Data) Rewind()                 { C.pn_data_rewind(d.pn) }
+func (d Data) Error() error            { return pnError(C.pn_data_error(d.pn)) }
+
+// State holds the state flags for an AMQP endpoint.
+type State byte
+
+func (s State) LocalUninitialized() bool { return s&C.PN_LOCAL_UNINIT != 0 }
+func (s State) LocalActive() bool        { return s&C.PN_LOCAL_ACTIVE != 0 }
+func (s State) LocalOpen() bool          { return s&C.PN_LOCAL_ACTIVE != 0 }
+func (s State) LocalClosed() bool        { return s&C.PN_LOCAL_CLOSED != 0 }
+
+func (s State) RemoteUninitialized() bool { return s&C.PN_REMOTE_UNINIT != 0 }
+func (s State) RemoteActive() bool        { return s&C.PN_REMOTE_ACTIVE != 0 }
+func (s State) RemoteOpen() bool          { return s&C.PN_REMOTE_ACTIVE != 0 }
+func (s State) RemoteClosed() bool        { return s&C.PN_REMOTE_CLOSED != 0 }
+
+// Return a State containig just the local flags
+func (s State) Local() State { return State(s & C.PN_LOCAL_MASK) }
+
+// Return a State containig just the remote flags
+func (s State) Remote() State { return State(s & C.PN_REMOTE_MASK) }
+
+// Endpoint is the common interface for Connection, Link and Session.
+type Endpoint interface {
+	State() State
+	Open()
+	Close()
+	Condition() Condition
+	RemoteCondition() Condition
+}
+
+// Disposition types
+const (
+	Received uint64 = C.PN_RECEIVED
+	Accepted        = C.PN_ACCEPTED
+	Rejected        = C.PN_REJECTED
+	Released        = C.PN_RELEASED
+	Modified        = C.PN_MODIFIED
+)
+
+// SettleAs is equivalent to d.Update(disposition); d.Settle()
+// It is a no-op if e does not have a delivery.
+func (d Delivery) SettleAs(disposition uint64) {
+	d.Update(disposition)
+	d.Settle()
+}
+
+// Accept accepts and settles a delivery.
+func (d Delivery) Accept() { d.SettleAs(Accepted) }
+
+// Reject rejects and settles a delivery
+func (d Delivery) Reject() { d.SettleAs(Rejected) }
+
+// Release releases and settles a delivery
+// If delivered is true the delivery count for the message will be increased.
+func (d Delivery) Release(delivered bool) {
+	if delivered {
+		d.SettleAs(Modified)
+	} else {
+		d.SettleAs(Released)
+	}
+}
+
+type Transport struct{ pn *C.pn_transport_t }
+type DeliveryTag struct{ pn C.pn_delivery_tag_t } // FIXME aconway 2015-03-25: convert to string
+
+func (l Link) Recv(buf []byte) int {
+	if len(buf) == 0 {
+		return 0
+	}
+	return int(C.pn_link_recv(l.pn, (*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf))))
+}
+
+func (l Link) Send(bytes []byte) int {
+	return int(C.pn_link_send(l.pn, cPtr(bytes), cLen(bytes)))
+}
+
+func pnTag(tag string) C.pn_delivery_tag_t {
+	bytes := []byte(tag)
+	return C.pn_dtag(cPtr(bytes), cLen(bytes))
+}
+
+func (l Link) Delivery(tag string) Delivery {
+	return Delivery{C.pn_delivery(l.pn, pnTag(tag))}
+}
+
+func cPtr(b []byte) *C.char {
+	if len(b) == 0 {
+		return nil
+	}
+	return (*C.char)(unsafe.Pointer(&b[0]))
+}
+
+func cLen(b []byte) C.size_t {
+	return C.size_t(len(b))
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/d6f1b837/proton-c/bindings/go/src/qpid.apache.org/proton/event/wrappers_gen.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/event/wrappers_gen.go b/proton-c/bindings/go/src/qpid.apache.org/proton/event/wrappers_gen.go
new file mode 100644
index 0000000..f653525
--- /dev/null
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/event/wrappers_gen.go
@@ -0,0 +1,505 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+//
+// NOTE: This file was generated by genwrap.go, do not edit it by hand.
+//
+
+package event
+
+import (
+	"time"
+)
+
+// #include <proton/types.h>
+// #include <proton/event.h>
+// #include <proton/session.h>
+// #include <proton/link.h>
+// #include <proton/delivery.h>
+// #include <proton/disposition.h>
+// #include <proton/condition.h>
+// #include <proton/terminus.h>
+// #include <proton/connection.h>
+import "C"
+
+type Event struct{ pn *C.pn_event_t }
+
+func (e Event) isNil() bool            { return e.pn == nil }
+func (e Event) Type() EventType        { return EventType(C.pn_event_type(e.pn)) }
+func (e Event) Connection() Connection { return Connection{C.pn_event_connection(e.pn)} }
+func (e Event) Session() Session       { return Session{C.pn_event_session(e.pn)} }
+func (e Event) Link() Link             { return Link{C.pn_event_link(e.pn)} }
+func (e Event) Delivery() Delivery     { return Delivery{C.pn_event_delivery(e.pn)} }
+func (e Event) Transport() Transport   { return Transport{C.pn_event_transport(e.pn)} }
+func (e Event) String() string         { return e.Type().String() }
+
+type EventType int
+
+const (
+	EConnectionInit        EventType = C.PN_CONNECTION_INIT
+	EConnectionBound       EventType = C.PN_CONNECTION_BOUND
+	EConnectionUnbound     EventType = C.PN_CONNECTION_UNBOUND
+	EConnectionLocalOpen   EventType = C.PN_CONNECTION_LOCAL_OPEN
+	EConnectionRemoteOpen  EventType = C.PN_CONNECTION_REMOTE_OPEN
+	EConnectionLocalClose  EventType = C.PN_CONNECTION_LOCAL_CLOSE
+	EConnectionRemoteClose EventType = C.PN_CONNECTION_REMOTE_CLOSE
+	EConnectionFinal       EventType = C.PN_CONNECTION_FINAL
+	ESessionInit           EventType = C.PN_SESSION_INIT
+	ESessionLocalOpen      EventType = C.PN_SESSION_LOCAL_OPEN
+	ESessionRemoteOpen     EventType = C.PN_SESSION_REMOTE_OPEN
+	ESessionLocalClose     EventType = C.PN_SESSION_LOCAL_CLOSE
+	ESessionRemoteClose    EventType = C.PN_SESSION_REMOTE_CLOSE
+	ESessionFinal          EventType = C.PN_SESSION_FINAL
+	ELinkInit              EventType = C.PN_LINK_INIT
+	ELinkLocalOpen         EventType = C.PN_LINK_LOCAL_OPEN
+	ELinkRemoteOpen        EventType = C.PN_LINK_REMOTE_OPEN
+	ELinkLocalClose        EventType = C.PN_LINK_LOCAL_CLOSE
+	ELinkRemoteClose       EventType = C.PN_LINK_REMOTE_CLOSE
+	ELinkLocalDetach       EventType = C.PN_LINK_LOCAL_DETACH
+	ELinkRemoteDetach      EventType = C.PN_LINK_REMOTE_DETACH
+	ELinkFlow              EventType = C.PN_LINK_FLOW
+	ELinkFinal             EventType = C.PN_LINK_FINAL
+	EDelivery              EventType = C.PN_DELIVERY
+	ETransport             EventType = C.PN_TRANSPORT
+	ETransportError        EventType = C.PN_TRANSPORT_ERROR
+	ETransportHeadClosed   EventType = C.PN_TRANSPORT_HEAD_CLOSED
+	ETransportTailClosed   EventType = C.PN_TRANSPORT_TAIL_CLOSED
+	ETransportClosed       EventType = C.PN_TRANSPORT_CLOSED
+)
+
+func (e EventType) String() string {
+	switch e {
+
+	case C.PN_CONNECTION_INIT:
+		return "ConnectionInit"
+	case C.PN_CONNECTION_BOUND:
+		return "ConnectionBound"
+	case C.PN_CONNECTION_UNBOUND:
+		return "ConnectionUnbound"
+	case C.PN_CONNECTION_LOCAL_OPEN:
+		return "ConnectionLocalOpen"
+	case C.PN_CONNECTION_REMOTE_OPEN:
+		return "ConnectionRemoteOpen"
+	case C.PN_CONNECTION_LOCAL_CLOSE:
+		return "ConnectionLocalClose"
+	case C.PN_CONNECTION_REMOTE_CLOSE:
+		return "ConnectionRemoteClose"
+	case C.PN_CONNECTION_FINAL:
+		return "ConnectionFinal"
+	case C.PN_SESSION_INIT:
+		return "SessionInit"
+	case C.PN_SESSION_LOCAL_OPEN:
+		return "SessionLocalOpen"
+	case C.PN_SESSION_REMOTE_OPEN:
+		return "SessionRemoteOpen"
+	case C.PN_SESSION_LOCAL_CLOSE:
+		return "SessionLocalClose"
+	case C.PN_SESSION_REMOTE_CLOSE:
+		return "SessionRemoteClose"
+	case C.PN_SESSION_FINAL:
+		return "SessionFinal"
+	case C.PN_LINK_INIT:
+		return "LinkInit"
+	case C.PN_LINK_LOCAL_OPEN:
+		return "LinkLocalOpen"
+	case C.PN_LINK_REMOTE_OPEN:
+		return "LinkRemoteOpen"
+	case C.PN_LINK_LOCAL_CLOSE:
+		return "LinkLocalClose"
+	case C.PN_LINK_REMOTE_CLOSE:
+		return "LinkRemoteClose"
+	case C.PN_LINK_LOCAL_DETACH:
+		return "LinkLocalDetach"
+	case C.PN_LINK_REMOTE_DETACH:
+		return "LinkRemoteDetach"
+	case C.PN_LINK_FLOW:
+		return "LinkFlow"
+	case C.PN_LINK_FINAL:
+		return "LinkFinal"
+	case C.PN_DELIVERY:
+		return "Delivery"
+	case C.PN_TRANSPORT:
+		return "Transport"
+	case C.PN_TRANSPORT_ERROR:
+		return "TransportError"
+	case C.PN_TRANSPORT_HEAD_CLOSED:
+		return "TransportHeadClosed"
+	case C.PN_TRANSPORT_TAIL_CLOSED:
+		return "TransportTailClosed"
+	case C.PN_TRANSPORT_CLOSED:
+		return "TransportClosed"
+	}
+	return "Unknown"
+}
+
+// Wrappers for declarations in session.h
+
+type Session struct{ pn *C.pn_session_t }
+
+func (s Session) isNil() bool                { return s.pn == nil }
+func (s Session) Free()                      { C.pn_session_free(s.pn) }
+func (s Session) State() State               { return State(C.pn_session_state(s.pn)) }
+func (s Session) Error() error               { return pnError(C.pn_session_error(s.pn)) }
+func (s Session) Condition() Condition       { return Condition{C.pn_session_condition(s.pn)} }
+func (s Session) RemoteCondition() Condition { return Condition{C.pn_session_remote_condition(s.pn)} }
+func (s Session) Connection() Connection     { return Connection{C.pn_session_connection(s.pn)} }
+func (s Session) Open()                      { C.pn_session_open(s.pn) }
+func (s Session) Close()                     { C.pn_session_close(s.pn) }
+func (s Session) IncomingCapacity() uint     { return uint(C.pn_session_get_incoming_capacity(s.pn)) }
+func (s Session) SetIncomingCapacity(capacity uint) {
+	C.pn_session_set_incoming_capacity(s.pn, C.size_t(capacity))
+}
+func (s Session) OutgoingBytes() uint { return uint(C.pn_session_outgoing_bytes(s.pn)) }
+func (s Session) IncomingBytes() uint { return uint(C.pn_session_incoming_bytes(s.pn)) }
+func (s Session) Next(state State) Session {
+	return Session{C.pn_session_next(s.pn, C.pn_state_t(state))}
+}
+
+// Wrappers for declarations in link.h
+
+type SndSettleMode C.pn_snd_settle_mode_t
+
+const (
+	PnSndUnsettled SndSettleMode = C.PN_SND_UNSETTLED
+	PnSndSettled   SndSettleMode = C.PN_SND_SETTLED
+	PnSndMixed     SndSettleMode = C.PN_SND_MIXED
+)
+
+func (e SndSettleMode) String() string {
+	switch e {
+
+	case C.PN_SND_UNSETTLED:
+		return "PnSndUnsettled"
+	case C.PN_SND_SETTLED:
+		return "PnSndSettled"
+	case C.PN_SND_MIXED:
+		return "PnSndMixed"
+	}
+	return "unknown"
+}
+
+type RcvSettleMode C.pn_rcv_settle_mode_t
+
+const (
+	PnRcvFirst  RcvSettleMode = C.PN_RCV_FIRST
+	PnRcvSecond RcvSettleMode = C.PN_RCV_SECOND
+)
+
+func (e RcvSettleMode) String() string {
+	switch e {
+
+	case C.PN_RCV_FIRST:
+		return "PnRcvFirst"
+	case C.PN_RCV_SECOND:
+		return "PnRcvSecond"
+	}
+	return "unknown"
+}
+
+type Link struct{ pn *C.pn_link_t }
+
+func (l Link) isNil() bool                  { return l.pn == nil }
+func (l Link) Free()                        { C.pn_link_free(l.pn) }
+func (l Link) Name() string                 { return C.GoString(C.pn_link_name(l.pn)) }
+func (l Link) IsSender() bool               { return bool(C.pn_link_is_sender(l.pn)) }
+func (l Link) IsReceiver() bool             { return bool(C.pn_link_is_receiver(l.pn)) }
+func (l Link) State() State                 { return State(C.pn_link_state(l.pn)) }
+func (l Link) Error() error                 { return pnError(C.pn_link_error(l.pn)) }
+func (l Link) Condition() Condition         { return Condition{C.pn_link_condition(l.pn)} }
+func (l Link) RemoteCondition() Condition   { return Condition{C.pn_link_remote_condition(l.pn)} }
+func (l Link) Session() Session             { return Session{C.pn_link_session(l.pn)} }
+func (l Link) Next(state State) Link        { return Link{C.pn_link_next(l.pn, C.pn_state_t(state))} }
+func (l Link) Open()                        { C.pn_link_open(l.pn) }
+func (l Link) Close()                       { C.pn_link_close(l.pn) }
+func (l Link) Detach()                      { C.pn_link_detach(l.pn) }
+func (l Link) Source() Terminus             { return Terminus{C.pn_link_source(l.pn)} }
+func (l Link) Target() Terminus             { return Terminus{C.pn_link_target(l.pn)} }
+func (l Link) RemoteSource() Terminus       { return Terminus{C.pn_link_remote_source(l.pn)} }
+func (l Link) RemoteTarget() Terminus       { return Terminus{C.pn_link_remote_target(l.pn)} }
+func (l Link) Current() Delivery            { return Delivery{C.pn_link_current(l.pn)} }
+func (l Link) Advance() bool                { return bool(C.pn_link_advance(l.pn)) }
+func (l Link) Credit() int                  { return int(C.pn_link_credit(l.pn)) }
+func (l Link) Queued() int                  { return int(C.pn_link_queued(l.pn)) }
+func (l Link) RemoteCredit() int            { return int(C.pn_link_remote_credit(l.pn)) }
+func (l Link) IsDrain() bool                { return bool(C.pn_link_get_drain(l.pn)) }
+func (l Link) Drained() int                 { return int(C.pn_link_drained(l.pn)) }
+func (l Link) Available() int               { return int(C.pn_link_available(l.pn)) }
+func (l Link) SndSettleMode() SndSettleMode { return SndSettleMode(C.pn_link_snd_settle_mode(l.pn)) }
+func (l Link) RcvSettleMode() RcvSettleMode { return RcvSettleMode(C.pn_link_rcv_settle_mode(l.pn)) }
+func (l Link) SetSndSettleMode(mode SndSettleMode) {
+	C.pn_link_set_snd_settle_mode(l.pn, C.pn_snd_settle_mode_t(mode))
+}
+func (l Link) SetRcvSettleMode(mode RcvSettleMode) {
+	C.pn_link_set_rcv_settle_mode(l.pn, C.pn_rcv_settle_mode_t(mode))
+}
+func (l Link) RemoteSndSettleMode() SndSettleMode {
+	return SndSettleMode(C.pn_link_remote_snd_settle_mode(l.pn))
+}
+func (l Link) RemoteRcvSettleMode() RcvSettleMode {
+	return RcvSettleMode(C.pn_link_remote_rcv_settle_mode(l.pn))
+}
+func (l Link) Unsettled() int      { return int(C.pn_link_unsettled(l.pn)) }
+func (l Link) Offered(credit int)  { C.pn_link_offered(l.pn, C.int(credit)) }
+func (l Link) Flow(credit int)     { C.pn_link_flow(l.pn, C.int(credit)) }
+func (l Link) Drain(credit int)    { C.pn_link_drain(l.pn, C.int(credit)) }
+func (l Link) SetDrain(drain bool) { C.pn_link_set_drain(l.pn, C.bool(drain)) }
+func (l Link) Draining() bool      { return bool(C.pn_link_draining(l.pn)) }
+
+// Wrappers for declarations in delivery.h
+
+type Delivery struct{ pn *C.pn_delivery_t }
+
+func (d Delivery) isNil() bool         { return d.pn == nil }
+func (d Delivery) Tag() DeliveryTag    { return DeliveryTag{C.pn_delivery_tag(d.pn)} }
+func (d Delivery) Link() Link          { return Link{C.pn_delivery_link(d.pn)} }
+func (d Delivery) Local() Disposition  { return Disposition{C.pn_delivery_local(d.pn)} }
+func (d Delivery) LocalState() uint64  { return uint64(C.pn_delivery_local_state(d.pn)) }
+func (d Delivery) Remote() Disposition { return Disposition{C.pn_delivery_remote(d.pn)} }
+func (d Delivery) RemoteState() uint64 { return uint64(C.pn_delivery_remote_state(d.pn)) }
+func (d Delivery) Settled() bool       { return bool(C.pn_delivery_settled(d.pn)) }
+func (d Delivery) Pending() uint       { return uint(C.pn_delivery_pending(d.pn)) }
+func (d Delivery) Partial() bool       { return bool(C.pn_delivery_partial(d.pn)) }
+func (d Delivery) Writable() bool      { return bool(C.pn_delivery_writable(d.pn)) }
+func (d Delivery) Readable() bool      { return bool(C.pn_delivery_readable(d.pn)) }
+func (d Delivery) Updated() bool       { return bool(C.pn_delivery_updated(d.pn)) }
+func (d Delivery) Update(state uint64) { C.pn_delivery_update(d.pn, C.uint64_t(state)) }
+func (d Delivery) Clear()              { C.pn_delivery_clear(d.pn) }
+func (d Delivery) Settle()             { C.pn_delivery_settle(d.pn) }
+func (d Delivery) Dump()               { C.pn_delivery_dump(d.pn) }
+func (d Delivery) Buffered() bool      { return bool(C.pn_delivery_buffered(d.pn)) }
+
+// Wrappers for declarations in disposition.h
+
+type Disposition struct{ pn *C.pn_disposition_t }
+
+func (d Disposition) isNil() bool           { return d.pn == nil }
+func (d Disposition) Type() uint64          { return uint64(C.pn_disposition_type(d.pn)) }
+func (d Disposition) Condition() Condition  { return Condition{C.pn_disposition_condition(d.pn)} }
+func (d Disposition) Data() Data            { return Data{C.pn_disposition_data(d.pn)} }
+func (d Disposition) SectionNumber() uint32 { return uint32(C.pn_disposition_get_section_number(d.pn)) }
+func (d Disposition) SetSectionNumber(section_number uint32) {
+	C.pn_disposition_set_section_number(d.pn, C.uint32_t(section_number))
+}
+func (d Disposition) SectionOffset() uint64 { return uint64(C.pn_disposition_get_section_offset(d.pn)) }
+func (d Disposition) SetSectionOffset(section_offset uint64) {
+	C.pn_disposition_set_section_offset(d.pn, C.uint64_t(section_offset))
+}
+func (d Disposition) IsFailed() bool        { return bool(C.pn_disposition_is_failed(d.pn)) }
+func (d Disposition) SetFailed(failed bool) { C.pn_disposition_set_failed(d.pn, C.bool(failed)) }
+func (d Disposition) IsUndeliverable() bool { return bool(C.pn_disposition_is_undeliverable(d.pn)) }
+func (d Disposition) SetUndeliverable(undeliverable bool) {
+	C.pn_disposition_set_undeliverable(d.pn, C.bool(undeliverable))
+}
+func (d Disposition) Annotations() Data { return Data{C.pn_disposition_annotations(d.pn)} }
+
+// Wrappers for declarations in condition.h
+
+type Condition struct{ pn *C.pn_condition_t }
+
+func (c Condition) isNil() bool  { return c.pn == nil }
+func (c Condition) IsSet() bool  { return bool(C.pn_condition_is_set(c.pn)) }
+func (c Condition) Clear()       { C.pn_condition_clear(c.pn) }
+func (c Condition) Name() string { return C.GoString(C.pn_condition_get_name(c.pn)) }
+func (c Condition) SetName(name string) int {
+	return int(C.pn_condition_set_name(c.pn, C.CString(name)))
+}
+func (c Condition) Description() string { return C.GoString(C.pn_condition_get_description(c.pn)) }
+func (c Condition) SetDescription(description string) int {
+	return int(C.pn_condition_set_description(c.pn, C.CString(description)))
+}
+func (c Condition) Info() Data           { return Data{C.pn_condition_info(c.pn)} }
+func (c Condition) IsRedirect() bool     { return bool(C.pn_condition_is_redirect(c.pn)) }
+func (c Condition) RedirectHost() string { return C.GoString(C.pn_condition_redirect_host(c.pn)) }
+func (c Condition) RedirectPort() int    { return int(C.pn_condition_redirect_port(c.pn)) }
+
+// Wrappers for declarations in terminus.h
+
+type TerminusType C.pn_terminus_type_t
+
+const (
+	PnUnspecified TerminusType = C.PN_UNSPECIFIED
+	PnSource      TerminusType = C.PN_SOURCE
+	PnTarget      TerminusType = C.PN_TARGET
+	PnCoordinator TerminusType = C.PN_COORDINATOR
+)
+
+func (e TerminusType) String() string {
+	switch e {
+
+	case C.PN_UNSPECIFIED:
+		return "PnUnspecified"
+	case C.PN_SOURCE:
+		return "PnSource"
+	case C.PN_TARGET:
+		return "PnTarget"
+	case C.PN_COORDINATOR:
+		return "PnCoordinator"
+	}
+	return "unknown"
+}
+
+type Durability C.pn_durability_t
+
+const (
+	PnNondurable    Durability = C.PN_NONDURABLE
+	PnConfiguration Durability = C.PN_CONFIGURATION
+	PnDeliveries    Durability = C.PN_DELIVERIES
+)
+
+func (e Durability) String() string {
+	switch e {
+
+	case C.PN_NONDURABLE:
+		return "PnNondurable"
+	case C.PN_CONFIGURATION:
+		return "PnConfiguration"
+	case C.PN_DELIVERIES:
+		return "PnDeliveries"
+	}
+	return "unknown"
+}
+
+type ExpiryPolicy C.pn_expiry_policy_t
+
+const (
+	PnExpireWithLink       ExpiryPolicy = C.PN_EXPIRE_WITH_LINK
+	PnExpireWithSession    ExpiryPolicy = C.PN_EXPIRE_WITH_SESSION
+	PnExpireWithConnection ExpiryPolicy = C.PN_EXPIRE_WITH_CONNECTION
+	PnExpireNever          ExpiryPolicy = C.PN_EXPIRE_NEVER
+)
+
+func (e ExpiryPolicy) String() string {
+	switch e {
+
+	case C.PN_EXPIRE_WITH_LINK:
+		return "PnExpireWithLink"
+	case C.PN_EXPIRE_WITH_SESSION:
+		return "PnExpireWithSession"
+	case C.PN_EXPIRE_WITH_CONNECTION:
+		return "PnExpireWithConnection"
+	case C.PN_EXPIRE_NEVER:
+		return "PnExpireNever"
+	}
+	return "unknown"
+}
+
+type DistributionMode C.pn_distribution_mode_t
+
+const (
+	PnDistModeUnspecified DistributionMode = C.PN_DIST_MODE_UNSPECIFIED
+	PnDistModeCopy        DistributionMode = C.PN_DIST_MODE_COPY
+	PnDistModeMove        DistributionMode = C.PN_DIST_MODE_MOVE
+)
+
+func (e DistributionMode) String() string {
+	switch e {
+
+	case C.PN_DIST_MODE_UNSPECIFIED:
+		return "PnDistModeUnspecified"
+	case C.PN_DIST_MODE_COPY:
+		return "PnDistModeCopy"
+	case C.PN_DIST_MODE_MOVE:
+		return "PnDistModeMove"
+	}
+	return "unknown"
+}
+
+type Terminus struct{ pn *C.pn_terminus_t }
+
+func (t Terminus) isNil() bool        { return t.pn == nil }
+func (t Terminus) Type() TerminusType { return TerminusType(C.pn_terminus_get_type(t.pn)) }
+func (t Terminus) SetType(type_ TerminusType) int {
+	return int(C.pn_terminus_set_type(t.pn, C.pn_terminus_type_t(type_)))
+}
+func (t Terminus) Address() string { return C.GoString(C.pn_terminus_get_address(t.pn)) }
+func (t Terminus) SetAddress(address string) int {
+	return int(C.pn_terminus_set_address(t.pn, C.CString(address)))
+}
+func (t Terminus) SetDistributionMode(mode DistributionMode) int {
+	return int(C.pn_terminus_set_distribution_mode(t.pn, C.pn_distribution_mode_t(mode)))
+}
+func (t Terminus) Durability() Durability { return Durability(C.pn_terminus_get_durability(t.pn)) }
+func (t Terminus) SetDurability(durability Durability) int {
+	return int(C.pn_terminus_set_durability(t.pn, C.pn_durability_t(durability)))
+}
+func (t Terminus) ExpiryPolicy() ExpiryPolicy {
+	return ExpiryPolicy(C.pn_terminus_get_expiry_policy(t.pn))
+}
+func (t Terminus) SetExpiryPolicy(policy ExpiryPolicy) int {
+	return int(C.pn_terminus_set_expiry_policy(t.pn, C.pn_expiry_policy_t(policy)))
+}
+func (t Terminus) Timeout() time.Duration {
+	return (time.Duration(C.pn_terminus_get_timeout(t.pn)) * time.Second)
+}
+func (t Terminus) SetTimeout(timeout time.Duration) int {
+	return int(C.pn_terminus_set_timeout(t.pn, C.pn_seconds_t(timeout)))
+}
+func (t Terminus) IsDynamic() bool { return bool(C.pn_terminus_is_dynamic(t.pn)) }
+func (t Terminus) SetDynamic(dynamic bool) int {
+	return int(C.pn_terminus_set_dynamic(t.pn, C.bool(dynamic)))
+}
+func (t Terminus) Properties() Data      { return Data{C.pn_terminus_properties(t.pn)} }
+func (t Terminus) Capabilities() Data    { return Data{C.pn_terminus_capabilities(t.pn)} }
+func (t Terminus) Outcomes() Data        { return Data{C.pn_terminus_outcomes(t.pn)} }
+func (t Terminus) Filter() Data          { return Data{C.pn_terminus_filter(t.pn)} }
+func (t Terminus) Copy(src Terminus) int { return int(C.pn_terminus_copy(t.pn, src.pn)) }
+
+// Wrappers for declarations in connection.h
+
+type Connection struct{ pn *C.pn_connection_t }
+
+func (c Connection) isNil() bool          { return c.pn == nil }
+func (c Connection) Free()                { C.pn_connection_free(c.pn) }
+func (c Connection) Release()             { C.pn_connection_release(c.pn) }
+func (c Connection) Error() error         { return pnError(C.pn_connection_error(c.pn)) }
+func (c Connection) State() State         { return State(C.pn_connection_state(c.pn)) }
+func (c Connection) Open()                { C.pn_connection_open(c.pn) }
+func (c Connection) Close()               { C.pn_connection_close(c.pn) }
+func (c Connection) Reset()               { C.pn_connection_reset(c.pn) }
+func (c Connection) Condition() Condition { return Condition{C.pn_connection_condition(c.pn)} }
+func (c Connection) RemoteCondition() Condition {
+	return Condition{C.pn_connection_remote_condition(c.pn)}
+}
+func (c Connection) Container() string { return C.GoString(C.pn_connection_get_container(c.pn)) }
+func (c Connection) SetContainer(container string) {
+	C.pn_connection_set_container(c.pn, C.CString(container))
+}
+func (c Connection) Hostname() string { return C.GoString(C.pn_connection_get_hostname(c.pn)) }
+func (c Connection) SetHostname(hostname string) {
+	C.pn_connection_set_hostname(c.pn, C.CString(hostname))
+}
+func (c Connection) RemoteContainer() string {
+	return C.GoString(C.pn_connection_remote_container(c.pn))
+}
+func (c Connection) RemoteHostname() string { return C.GoString(C.pn_connection_remote_hostname(c.pn)) }
+func (c Connection) OfferedCapabilities() Data {
+	return Data{C.pn_connection_offered_capabilities(c.pn)}
+}
+func (c Connection) DesiredCapabilities() Data {
+	return Data{C.pn_connection_desired_capabilities(c.pn)}
+}
+func (c Connection) Properties() Data { return Data{C.pn_connection_properties(c.pn)} }
+func (c Connection) RemoteOfferedCapabilities() Data {
+	return Data{C.pn_connection_remote_offered_capabilities(c.pn)}
+}
+func (c Connection) RemoteDesiredCapabilities() Data {
+	return Data{C.pn_connection_remote_desired_capabilities(c.pn)}
+}
+func (c Connection) RemoteProperties() Data { return Data{C.pn_connection_remote_properties(c.pn)} }
+func (c Connection) Transport() Transport   { return Transport{C.pn_connection_transport(c.pn)} }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


Mime
View raw message