You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2016/11/02 02:51:21 UTC

qpid-proton git commit: PROTON-1338: Go: make binding compatible with older C libraries

Repository: qpid-proton
Updated Branches:
  refs/heads/master 9a4cdf0f4 -> 7b6b8dec7


PROTON-1338: Go: make binding compatible with older C libraries

The Go binding is now compatible with all releases since 0.10, tested up to 0.15
Absent C library source or binary incompatible changes, it should remain so.


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

Branch: refs/heads/master
Commit: 7b6b8dec76f1345647de90201130f7d8bff903f1
Parents: 9a4cdf0
Author: Alan Conway <ac...@redhat.com>
Authored: Tue Nov 1 22:47:35 2016 -0400
Committer: Alan Conway <ac...@redhat.com>
Committed: Tue Nov 1 22:47:35 2016 -0400

----------------------------------------------------------------------
 proton-c/bindings/go/genwrap.go                 |   9 +-
 .../go/src/qpid.apache.org/amqp/interop_test.go |  29 ++--
 .../go/src/qpid.apache.org/amqp/version.go      |  29 ++++
 .../go/src/qpid.apache.org/proton/engine.go     | 143 ++++++++++++-------
 .../go/src/qpid.apache.org/proton/handlers.go   |   6 +-
 .../src/qpid.apache.org/proton/proton_test.go   |  73 +++++++++-
 .../go/src/qpid.apache.org/proton/wrappers.go   |  10 ++
 .../src/qpid.apache.org/proton/wrappers_gen.go  |   6 -
 8 files changed, 219 insertions(+), 86 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/7b6b8dec/proton-c/bindings/go/genwrap.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/genwrap.go b/proton-c/bindings/go/genwrap.go
index 8a9af03..7782b0b 100644
--- a/proton-c/bindings/go/genwrap.go
+++ b/proton-c/bindings/go/genwrap.go
@@ -212,7 +212,7 @@ 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|link_recv|link_send|transport_.*logf$|transport_.*trace|transport_head|transport_push|connection_set_password")
+	skipFnRe    = regexp.MustCompile("attach|context|class|collect|link_recv|link_send|transport_.*logf$|transport_.*trace|transport_head|transport_tail|transport_push|connection_set_password|link_get_drain")
 )
 
 // Generate event wrappers.
@@ -410,12 +410,7 @@ func goFnName(api, fname string) string {
 	if skipFnRe.FindStringSubmatch(api+"_"+fname) != nil {
 		return ""
 	}
-	switch api + "." + fname {
-	case "link.get_drain":
-		return "IsDrain"
-	default:
-		return mixedCaseTrim(fname, "get_")
-	}
+	return mixedCaseTrim(fname, "get_")
 }
 
 func apiWrapFns(api, header string, out io.Writer) {

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/7b6b8dec/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go
index d118523..b3e27bc 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go
@@ -40,11 +40,14 @@ func checkEqual(want interface{}, got interface{}) error {
 	return nil
 }
 
-func getReader(name string) (r io.Reader) {
+func getReader(t *testing.T, name string) (r io.Reader) {
 	dir := os.Getenv("PN_INTEROP_DIR")
+	if dir == "" {
+		t.Skip("no PN_INTEROP_DIR in environment")
+	}
 	r, err := os.Open(dir + "/" + name + ".amqp")
 	if err != nil {
-		panic(fmt.Errorf("Can't open %#v: %v", name, err))
+		t.Fatalf("can't open %#v: %v", name, err)
 	}
 	return
 }
@@ -91,7 +94,7 @@ func checkDecode(d *Decoder, want interface{}, gotPtr interface{}, t *testing.T)
 }
 
 func TestUnmarshal(t *testing.T) {
-	bytes, err := ioutil.ReadAll(getReader("strings"))
+	bytes, err := ioutil.ReadAll(getReader(t, "strings"))
 	if err != nil {
 		t.Error(err)
 	}
@@ -109,7 +112,7 @@ func TestUnmarshal(t *testing.T) {
 }
 
 func TestPrimitivesExact(t *testing.T) {
-	d := NewDecoder(getReader("primitives"))
+	d := NewDecoder(getReader(t, "primitives"))
 	// Decoding into exact types
 	var b bool
 	checkDecode(d, true, &b, t)
@@ -135,7 +138,7 @@ func TestPrimitivesExact(t *testing.T) {
 }
 
 func TestPrimitivesCompatible(t *testing.T) {
-	d := NewDecoder(getReader("primitives"))
+	d := NewDecoder(getReader(t, "primitives"))
 	// Decoding into compatible types
 	var b bool
 	var i int
@@ -188,7 +191,7 @@ func checkDecodeInterface(d *Decoder, want interface{}, t *testing.T) {
 }
 
 func TestPrimitivesInterface(t *testing.T) {
-	d := NewDecoder(getReader("primitives"))
+	d := NewDecoder(getReader(t, "primitives"))
 	checkDecodeInterface(d, true, t)
 	checkDecodeInterface(d, false, t)
 	checkDecodeInterface(d, uint8(42), t)
@@ -203,7 +206,7 @@ func TestPrimitivesInterface(t *testing.T) {
 }
 
 func TestStrings(t *testing.T) {
-	d := NewDecoder(getReader("strings"))
+	d := NewDecoder(getReader(t, "strings"))
 	// Test decoding as plain Go strings
 	for _, want := range []string{"abc\000defg", "abcdefg", "abcdefg", "", "", ""} {
 		var got string
@@ -215,7 +218,7 @@ func TestStrings(t *testing.T) {
 	}
 
 	// Test decoding as specific string types
-	d = NewDecoder(getReader("strings"))
+	d = NewDecoder(getReader(t, "strings"))
 	var bytes []byte
 	var str, sym string
 	checkDecode(d, []byte("abc\000defg"), &bytes, t)
@@ -230,7 +233,7 @@ func TestStrings(t *testing.T) {
 	}
 
 	// Test some error handling
-	d = NewDecoder(getReader("strings"))
+	d = NewDecoder(getReader(t, "strings"))
 	var s string
 	err := d.Decode(s)
 	if err == nil {
@@ -314,7 +317,7 @@ func TestEncodeDecode(t *testing.T) {
 }
 
 func TestMap(t *testing.T) {
-	d := NewDecoder(getReader("maps"))
+	d := NewDecoder(getReader(t, "maps"))
 
 	// Generic map
 	var m Map
@@ -324,7 +327,7 @@ func TestMap(t *testing.T) {
 	var i interface{}
 	checkDecode(d, Map{int32(1): "one", int32(2): "two", int32(3): "three"}, &i, t)
 
-	d = NewDecoder(getReader("maps"))
+	d = NewDecoder(getReader(t, "maps"))
 	// Specific typed map
 	var m2 map[string]int
 	checkDecode(d, map[string]int{"one": 1, "two": 2, "three": 3}, &m2, t)
@@ -345,7 +348,7 @@ func TestMap(t *testing.T) {
 }
 
 func TestList(t *testing.T) {
-	d := NewDecoder(getReader("lists"))
+	d := NewDecoder(getReader(t, "lists"))
 	var l List
 	checkDecode(d, List{int32(32), "foo", true}, &l, t)
 	checkDecode(d, List{}, &l, t)
@@ -354,7 +357,7 @@ func TestList(t *testing.T) {
 // TODO aconway 2015-09-08: the message.amqp file seems to be incorrectly coded as
 // as an AMQP string *inside* an AMQP binary?? Skip the test for now.
 func TODO_TestMessage(t *testing.T) {
-	bytes, err := ioutil.ReadAll(getReader("message"))
+	bytes, err := ioutil.ReadAll(getReader(t, "message"))
 	if err != nil {
 		t.Fatal(err)
 	}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/7b6b8dec/proton-c/bindings/go/src/qpid.apache.org/amqp/version.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/version.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/version.go
new file mode 100644
index 0000000..cefa904
--- /dev/null
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/version.go
@@ -0,0 +1,29 @@
+/*
+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 amqp
+
+// Version check for proton library.
+// Done here because this is the lowest-level dependency for all the proton Go packages.
+
+// #include <proton/version.h>
+// #if PN_VERSION_MINOR < 10
+// #error packages qpid.apache.org/... require Proton-C library version 0.10 or greater
+// #endif
+import "C"

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/7b6b8dec/proton-c/bindings/go/src/qpid.apache.org/proton/engine.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/engine.go b/proton-c/bindings/go/src/qpid.apache.org/proton/engine.go
index 5680010..c0f0093 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/proton/engine.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/engine.go
@@ -22,6 +22,8 @@ package proton
 import (
 	"fmt"
 	"net"
+	"os"
+	"strings"
 	"sync"
 	"time"
 	"unsafe"
@@ -29,7 +31,6 @@ import (
 
 /*
 #include <proton/connection.h>
-#include <proton/connection_engine.h>
 #include <proton/event.h>
 #include <proton/error.h>
 #include <proton/handlers.h>
@@ -102,52 +103,66 @@ type Engine struct {
 	err    ErrorHolder
 	inject chan func()
 
-	conn      net.Conn
-	engine    C.pn_connection_engine_t
-	handlers  []EventHandler // Handlers for proton events.
-	running   chan struct{}  // This channel will be closed when the goroutines are done.
-	closeOnce sync.Once
-	timer     *time.Timer
+	conn       net.Conn
+	connection Connection
+	transport  Transport
+	collector  *C.pn_collector_t
+	handlers   []EventHandler // Handlers for proton events.
+	running    chan struct{}  // This channel will be closed when the goroutines are done.
+	closeOnce  sync.Once
+	timer      *time.Timer
+	traceEvent bool
 }
 
 const bufferSize = 4096
 
-// NewEngine initializes a engine with a connection and handlers. To start it running:
-//    eng := NewEngine(...)
-//    go run eng.Run()
-// The goroutine will exit when the engine is closed or disconnected.
-// You can check for errors on Engine.Error.
-//
+func envBool(name string) bool {
+	v := strings.ToLower(os.Getenv(name))
+	return v == "true" || v == "1" || v == "yes" || v == "on"
+}
+
+// Create a new Engine and call Initialize() with conn and handlers
 func NewEngine(conn net.Conn, handlers ...EventHandler) (*Engine, error) {
-	eng := &Engine{
-		inject:   make(chan func()),
-		conn:     conn,
-		handlers: handlers,
-		running:  make(chan struct{}),
-		timer:    time.NewTimer(0),
-	}
-	if pnErr := C.pn_connection_engine_init(&eng.engine); pnErr != 0 {
-		return nil, fmt.Errorf("cannot setup engine: %s", PnErrorCode(pnErr))
+	eng := &Engine{}
+	return eng, eng.Initialize(conn, handlers...)
+}
+
+// Initialize an Engine with a connection and handlers. Start it with Run()
+func (eng *Engine) Initialize(conn net.Conn, handlers ...EventHandler) error {
+	eng.inject = make(chan func())
+	eng.conn = conn
+	eng.connection = Connection{C.pn_connection()}
+	eng.transport = Transport{C.pn_transport()}
+	eng.collector = C.pn_collector()
+	eng.handlers = handlers
+	eng.running = make(chan struct{})
+	eng.timer = time.NewTimer(0)
+	eng.traceEvent = envBool("PN_TRACE_EVT")
+	if eng.transport.IsNil() || eng.connection.IsNil() || eng.collector == nil {
+		eng.free()
+		return fmt.Errorf("proton.NewEngine cannot allocate")
 	}
-	return eng, nil
+	C.pn_connection_collect(eng.connection.pn, eng.collector)
+	return nil
 }
 
-// Create a byte slice backed by the memory of a pn_rwbytes_t, no copy.
-// Empty buffer {0,0} returns a nil byte slice.
-func byteSlice(data unsafe.Pointer, size C.size_t) []byte {
-	if data == nil || size == 0 {
+// Create a byte slice backed by C memory.
+// Empty or error (size <= 0) returns a nil byte slice.
+func cByteSlice(start unsafe.Pointer, size int) []byte {
+	if start == nil || size <= 0 {
 		return nil
 	} else {
-		return (*[1 << 30]byte)(data)[:size:size]
+		// Slice from very large imaginary array in C memory
+		return (*[1 << 30]byte)(start)[:size:size]
 	}
 }
 
 func (eng *Engine) Connection() Connection {
-	return Connection{C.pn_connection_engine_connection(&eng.engine)}
+	return eng.connection
 }
 
 func (eng *Engine) Transport() Transport {
-	return Transport{C.pn_connection_engine_transport(&eng.engine)}
+	return eng.transport
 }
 
 func (eng *Engine) String() string {
@@ -211,7 +226,8 @@ func (eng *Engine) disconnect(err error) {
 	cond := eng.Transport().Condition()
 	cond.SetError(err)              // Set the provided error.
 	cond.SetError(eng.conn.Close()) // Use connection error if cond is not already set.
-	C.pn_connection_engine_disconnected(&eng.engine)
+	eng.transport.CloseTail()
+	eng.transport.CloseHead()
 }
 
 // Close the engine's connection.
@@ -249,40 +265,59 @@ func (eng *Engine) tick() {
 }
 
 func (eng *Engine) dispatch() bool {
-	var needTick bool // Set if we need to tick the transport.
-	for {
-		cevent := C.pn_connection_engine_dispatch(&eng.engine)
-		if cevent == nil {
-			break
-		}
-		event := makeEvent(cevent, eng)
-		if event.Type() == ETransport {
-			needTick = true
+	for ce := C.pn_collector_peek(eng.collector); ce != nil; ce = C.pn_collector_peek(eng.collector) {
+		e := makeEvent(ce, eng)
+		if eng.traceEvent {
+			eng.transport.Log(e.String())
 		}
 		for _, h := range eng.handlers {
-			h.HandleEvent(event)
+			h.HandleEvent(e)
 		}
+		if e.Type() == EConnectionRemoteOpen {
+			eng.tick() // Update the tick if changed by remote.
+		}
+		C.pn_collector_pop(eng.collector)
 	}
-	if needTick {
-		eng.tick()
-	}
-	return !bool(C.pn_connection_engine_finished(&eng.engine))
+	return !eng.transport.Closed() || C.pn_collector_peek(eng.collector) != nil
 }
 
 func (eng *Engine) writeBuffer() []byte {
-	w := C.pn_connection_engine_write_buffer(&eng.engine)
-	return byteSlice(unsafe.Pointer(w.start), w.size)
+	size := eng.Transport().Pending() // Evaluate before Head(), may change buffer.
+	start := eng.Transport().Head()
+	return cByteSlice(start, size)
 }
 
 func (eng *Engine) readBuffer() []byte {
-	r := C.pn_connection_engine_read_buffer(&eng.engine)
-	return byteSlice(unsafe.Pointer(r.start), r.size)
+	size := eng.Transport().Capacity()
+	start := eng.Transport().Tail()
+	return cByteSlice(start, size)
+}
+
+func (eng *Engine) free() {
+	if !eng.transport.IsNil() {
+		eng.transport.Unbind()
+		eng.transport.Free()
+		eng.transport = Transport{}
+	}
+	if !eng.connection.IsNil() {
+		eng.connection.Free()
+		eng.connection = Connection{}
+	}
+	if eng.collector != nil {
+		C.pn_collector_release(eng.collector)
+		C.pn_collector_free(eng.collector)
+		eng.collector = nil
+	}
 }
 
 // Run the engine. Engine.Run() will exit when the engine is closed or
 // disconnected.  You can check for errors after exit with Engine.Error().
 //
 func (eng *Engine) Run() error {
+	defer eng.free()
+	eng.transport.Bind(eng.connection)
+	eng.tick() // Start ticking if needed
+
 	// Channels for read and write buffers going in and out of the read/write goroutines.
 	// The channels are unbuffered: we want to exchange buffers in seuquence.
 	readsIn, writesIn := make(chan []byte), make(chan []byte)
@@ -304,7 +339,7 @@ func (eng *Engine) Run() error {
 			} else if err != nil {
 				_ = eng.Inject(func() {
 					eng.Transport().Condition().SetError(err)
-					C.pn_connection_engine_read_close(&eng.engine)
+					eng.Transport().CloseTail()
 				})
 				return
 			}
@@ -324,7 +359,7 @@ func (eng *Engine) Run() error {
 			} else if err != nil {
 				_ = eng.Inject(func() {
 					eng.Transport().Condition().SetError(err)
-					C.pn_connection_engine_write_close(&eng.engine)
+					eng.Transport().CloseHead()
 				})
 				return
 			}
@@ -361,10 +396,10 @@ func (eng *Engine) Run() error {
 		case sendWrites <- writeBuf:
 
 		case buf := <-readsOut:
-			C.pn_connection_engine_read_done(&eng.engine, C.size_t(len(buf)))
+			eng.transport.Process(uint(len(buf)))
 
 		case buf := <-writesOut:
-			C.pn_connection_engine_write_done(&eng.engine, C.size_t(len(buf)))
+			eng.transport.Pop(uint(len(buf)))
 
 		case f, ok := <-eng.inject: // Function injected from another goroutine
 			if ok {
@@ -383,7 +418,5 @@ func (eng *Engine) Run() error {
 	close(eng.running)   // Signal goroutines have exited and Error is set, disable Inject()
 	_ = eng.conn.Close() // Close conn, force read/write goroutines to exit (they will Inject)
 	wait.Wait()          // Wait for goroutines
-
-	C.pn_connection_engine_final(&eng.engine)
 	return eng.err.Get()
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/7b6b8dec/proton-c/bindings/go/src/qpid.apache.org/proton/handlers.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/handlers.go b/proton-c/bindings/go/src/qpid.apache.org/proton/handlers.go
index 8213b65..961136e 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/proton/handlers.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/handlers.go
@@ -231,14 +231,14 @@ type flowcontroller struct {
 }
 
 func (d flowcontroller) HandleEvent(e Event) {
-	link := e.Link();
+	link := e.Link()
 
 	switch e.Type() {
 	case ELinkLocalOpen, ELinkRemoteOpen, ELinkFlow, EDelivery:
 		if link.IsReceiver() {
 			d.drained += link.Drained()
 			if d.drained != 0 {
-				link.Flow(d.window-link.Credit())
+				link.Flow(d.window - link.Credit())
 			}
 		}
 	}
@@ -313,7 +313,7 @@ func (d *MessagingAdapter) HandleEvent(e Event) {
 			d,
 		}
 		if d.Prefetch > 0 {
-			d.flowcontroller = flowcontroller{ window:d.Prefetch, drained:0 }
+			d.flowcontroller = flowcontroller{window: d.Prefetch, drained: 0}
 		}
 		d.mhandler.HandleMessagingEvent(MStart, e)
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/7b6b8dec/proton-c/bindings/go/src/qpid.apache.org/proton/proton_test.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/proton_test.go b/proton-c/bindings/go/src/qpid.apache.org/proton/proton_test.go
index bb3f21c..aad93eb 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/proton/proton_test.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/proton_test.go
@@ -20,8 +20,77 @@ under the License.
 package proton
 
 import (
+	"fmt"
+	"net"
+	"path"
+	"runtime"
 	"testing"
+	"time"
 )
 
-// TODO aconway 2015-10-14: placeholder, add unit tests.
-func Test(*testing.T) {}
+func errorIf(t *testing.T, err error) {
+	if err != nil {
+		_, file, line, ok := runtime.Caller(1) // annotate with location of caller.
+		if ok {
+			_, file = path.Split(file)
+		}
+		t.Errorf("(from %s:%d) %v", file, line, err)
+	}
+}
+
+func fatalIf(t *testing.T, err error) {
+	if err != nil {
+		_, file, line, ok := runtime.Caller(1) // annotate with location of caller.
+		if ok {
+			_, file = path.Split(file)
+		}
+		t.Fatalf("(from %s:%d) %v", file, line, err)
+	}
+}
+
+type events []EventType
+
+type testEngine struct {
+	Engine
+	events chan EventType
+}
+
+func newTestEngine(conn net.Conn) (*testEngine, error) {
+	testEng := &testEngine{events: make(chan EventType, 1000)}
+	return testEng, testEng.Initialize(conn, testEng)
+}
+
+func (eng *testEngine) HandleEvent(e Event) {
+	eng.events <- e.Type()
+}
+
+func (eng *testEngine) expect(events []EventType) error {
+	timer := time.After(5 * time.Second)
+	for _, want := range events {
+		select {
+		case got := <-eng.events:
+			if want != got {
+				return fmt.Errorf("want %s, got %s", want, got)
+			}
+		case <-timer:
+			return fmt.Errorf("expect timeout")
+		}
+	}
+	return nil
+}
+
+func Test(t *testing.T) {
+	cConn, sConn := net.Pipe()
+	client, err := newTestEngine(cConn)
+	fatalIf(t, err)
+	server, err := newTestEngine(sConn)
+	fatalIf(t, err)
+	server.Server()
+	go client.Run()
+	go server.Run()
+	fatalIf(t, server.expect(events{EConnectionInit, EConnectionBound}))
+	fatalIf(t, client.expect(events{EConnectionInit, EConnectionBound}))
+	fatalIf(t, client.InjectWait(func() error { client.Connection().Open(); return nil }))
+	fatalIf(t, client.expect(events{EConnectionLocalOpen}))
+	fatalIf(t, server.expect(events{EConnectionRemoteOpen}))
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/7b6b8dec/proton-c/bindings/go/src/qpid.apache.org/proton/wrappers.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/wrappers.go b/proton-c/bindings/go/src/qpid.apache.org/proton/wrappers.go
index 1dee743..4d55e4f 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/proton/wrappers.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/wrappers.go
@@ -268,6 +268,11 @@ func (l Link) Type() string {
 	}
 }
 
+// IsDrain calls pn_link_get_drain(), it conflicts with pn_link_drain() under the normal mapping.
+func (l Link) IsDrain() bool {
+	return bool(C.pn_link_get_drain(l.pn))
+}
+
 func cPtr(b []byte) *C.char {
 	if len(b) == 0 {
 		return nil
@@ -410,6 +415,11 @@ func (t Transport) Head() unsafe.Pointer {
 	return unsafe.Pointer(C.pn_transport_head(t.pn))
 }
 
+// Special treatment for Transport.Tail, return value is unsafe.Pointer not string
+func (t Transport) Tail() unsafe.Pointer {
+	return unsafe.Pointer(C.pn_transport_tail(t.pn))
+}
+
 // Special treatment for Transport.Push, takes []byte instead of char*, size
 func (t Transport) Push(bytes []byte) int {
 	return int(C.pn_transport_push(t.pn, (*C.char)(unsafe.Pointer(&bytes[0])), C.size_t(len(bytes))))

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/7b6b8dec/proton-c/bindings/go/src/qpid.apache.org/proton/wrappers_gen.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/proton/wrappers_gen.go b/proton-c/bindings/go/src/qpid.apache.org/proton/wrappers_gen.go
index 629caa6..19bfde2 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/proton/wrappers_gen.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/proton/wrappers_gen.go
@@ -310,9 +310,6 @@ func (l Link) Queued() int {
 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))
 }
@@ -832,9 +829,6 @@ func (t Transport) Output(bytes string, size uint) int {
 func (t Transport) Capacity() int {
 	return int(C.pn_transport_capacity(t.pn))
 }
-func (t Transport) Tail() string {
-	return C.GoString(C.pn_transport_tail(t.pn))
-}
 func (t Transport) Process(size uint) int {
 	return int(C.pn_transport_process(t.pn, C.size_t(size)))
 }


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