func (m *message) Decode(data []byte) error { m.Clear() if len(data) == 0 { return internal.Errorf("empty buffer for decode") } if C.pn_message_decode(m.pn, cPtr(data), cLen(data)) < 0 { return internal.Errorf("decoding message: %s", internal.PnError(unsafe.Pointer(C.pn_message_error(m.pn)))) } return nil }
// Message decodes the message containined in a delivery. // Will return an error if delivery.HasMessage() is false. func (delivery Delivery) Message() (m amqp.Message, err error) { if !delivery.Readable() || delivery.Partial() { return nil, internal.Errorf("attempting to get incomplete message") } data := make([]byte, delivery.Pending()) result := delivery.Link().Recv(data) if result != len(data) { return nil, internal.Errorf("cannot receive message: %s", internal.PnErrorCode(result)) } m = amqp.NewMessage() err = m.Decode(data) return }
func dataError(prefix string, data *C.pn_data_t) error { err := internal.PnError(unsafe.Pointer(C.pn_data_error(data))) if err != nil { err = internal.Errorf("%s: %s", prefix, err.(internal.Error)) } return err }
// ParseUrl parses an AMQP URL string and returns a net/url.Url. // // It is more forgiving than net/url.Parse and allows most of the parts of the // URL to be missing, assuming AMQP defaults. // func ParseURL(s string) (u *url.URL, err error) { cstr := C.CString(s) defer C.free(unsafe.Pointer(cstr)) pnUrl := C.pn_url_parse(cstr) if pnUrl == nil { return nil, internal.Errorf("bad URL %#v", s) } defer C.pn_url_free(pnUrl) scheme := C.GoString(C.pn_url_get_scheme(pnUrl)) username := C.GoString(C.pn_url_get_username(pnUrl)) password := C.GoString(C.pn_url_get_password(pnUrl)) host := C.GoString(C.pn_url_get_host(pnUrl)) port := C.GoString(C.pn_url_get_port(pnUrl)) path := C.GoString(C.pn_url_get_path(pnUrl)) if err != nil { return nil, internal.Errorf("bad URL %#v: %s", s, err) } if scheme == "" { scheme = amqp } if port == "" { if scheme == amqps { port = amqps } else { port = amqp } } var user *url.Userinfo if password != "" { user = url.UserPassword(username, password) } else if username != "" { user = url.User(username) } u = &url.URL{ Scheme: scheme, User: user, Host: net.JoinHostPort(host, port), Path: path, } return u, nil }
// decode from bytes. // Return bytes decoded or 0 if we could not decode a complete object. // func decode(data *C.pn_data_t, bytes []byte) int { if len(bytes) == 0 { return 0 } n := int(C.pn_data_decode(data, cPtr(bytes), cLen(bytes))) if n == int(C.PN_UNDERFLOW) { C.pn_error_clear(C.pn_data_error(data)) return 0 } else if n <= 0 { panic(internal.Errorf("unmarshal %s", internal.PnErrorCode(n))) } return n }
/* Unmarshal decodes AMQP-encoded bytes and stores the result in the value pointed to by v. Types are converted as follows: +---------------------------+----------------------------------------------------------------------+ |To Go types |From AMQP types | +===========================+======================================================================+ |bool |bool | +---------------------------+----------------------------------------------------------------------+ |int, int8, int16, |Equivalent or smaller signed integer type: byte, short, int, long. | |int32, int64 | | +---------------------------+----------------------------------------------------------------------+ |uint, uint8, uint16, |Equivalent or smaller unsigned integer type: ubyte, ushort, uint, | |uint32, uint64 types |ulong | +---------------------------+----------------------------------------------------------------------+ |float32, float64 |Equivalent or smaller float or double. | +---------------------------+----------------------------------------------------------------------+ |string, []byte |string, symbol or binary. | +---------------------------+----------------------------------------------------------------------+ |Symbol |symbol | +---------------------------+----------------------------------------------------------------------+ |map[K]T |map, provided all keys and values can unmarshal to types K, T | +---------------------------+----------------------------------------------------------------------+ |Map |map, any AMQP map | +---------------------------+----------------------------------------------------------------------+ |interface{} |Any AMQP value can be unmarshaled to an interface{} as follows: | | +------------------------+---------------------------------------------+ | |AMQP Type |Go Type in interface{} | | +========================+=============================================+ | |bool |bool | | +------------------------+---------------------------------------------+ | |byte,short,int,long |int8,int16,int32,int64 | | +------------------------+---------------------------------------------+ | |ubyte,ushort,uint,ulong |uint8,uint16,uint32,uint64 | | +------------------------+---------------------------------------------+ | |float, double |float32, float64 | | +------------------------+---------------------------------------------+ | |string |string | | +------------------------+---------------------------------------------+ | |symbol |Symbol | | +------------------------+---------------------------------------------+ | |binary |Binary | | +------------------------+---------------------------------------------+ | |nulll |nil | | +------------------------+---------------------------------------------+ | |map |Map | | +------------------------+---------------------------------------------+ | |list |List | +---------------------------+------------------------+---------------------------------------------+ The following Go types cannot be unmarshaled: uintptr, function, interface, channel. TODO Go types: array, struct. AMQP types: decimal32/64/128, char (round trip), timestamp, uuid, array, multi-section message bodies. AMQP maps with mixed/unhashable key types need an alternate representation. Described types. */ func Unmarshal(bytes []byte, v interface{}) (n int, err error) { defer doRecover(&err) data := C.pn_data(0) defer C.pn_data_free(data) n = decode(data, bytes) if n == 0 { err = internal.Errorf("not enough data") } else { unmarshal(v, data) } return }
// Send sends a amqp.Message over a Link. // Returns a Delivery that can be use to determine the outcome of the message. func (link Link) Send(m amqp.Message) (Delivery, error) { if !link.IsSender() { return Delivery{}, internal.Errorf("attempt to send message on receiving link") } delivery := link.Delivery(tags.Next()) bytes, err := m.Encode(nil) if err != nil { return Delivery{}, internal.Errorf("cannot send mesage %s", err) } result := link.SendBytes(bytes) link.Advance() if result != len(bytes) { if result < 0 { return delivery, internal.Errorf("send failed %v", internal.PnErrorCode(result)) } else { return delivery, internal.Errorf("send incomplete %v of %v", result, len(bytes)) } } if link.RemoteSndSettleMode() == SndSettled { delivery.Settle() } return delivery, nil }
func (m *message) Encode(buffer []byte) ([]byte, error) { encode := func(buf []byte) ([]byte, error) { len := cLen(buf) result := C.pn_message_encode(m.pn, cPtr(buf), &len) switch { case result == C.PN_OVERFLOW: return buf, overflow case result < 0: return buf, internal.Errorf("cannot encode message: %s", internal.PnErrorCode(result)) default: return buf[:len], nil } } return encodeGrow(buffer, encode) }
// HandleEvent handles an open/close event for an endpoint in a generic way. func (d endpointDelegator) HandleEvent(e Event) { endpoint := d.endpoint(e) state := endpoint.State() switch e.Type() { case d.localOpen: if state.RemoteActive() { d.delegator.mhandler.HandleMessagingEvent(d.opened, e) } case d.remoteOpen: switch { case state.LocalActive(): d.delegator.mhandler.HandleMessagingEvent(d.opened, e) case state.LocalUninit(): d.delegator.mhandler.HandleMessagingEvent(d.opening, e) if d.delegator.AutoOpen { endpoint.Open() } } case d.remoteClose: if endpoint.RemoteCondition().IsSet() { // Closed with error d.delegator.mhandler.HandleMessagingEvent(d.error, e) } else { d.delegator.mhandler.HandleMessagingEvent(d.closing, e) } if state.LocalClosed() { d.delegator.mhandler.HandleMessagingEvent(d.closed, e) } else if state.LocalActive() { endpoint.Close() } case d.localClose: if state.RemoteClosed() { d.delegator.mhandler.HandleMessagingEvent(d.closed, e) } default: // We shouldn't be called with any other event type. panic(internal.Errorf("internal error, not an open/close event: %s", e)) } }
// initLocal initializes a local link associated with a session. // Call in proton goroutine func makeLocalLink(sn *session, isSender bool, settings LinkSettings) (link, error) { var l link l.session = sn l.settings = settings l.isSender = isSender if l.settings.Name == "" { l.settings.Name = l.session.connection.container.nextLinkName() } if l.IsSender() { l.eLink = l.session.eSession.Sender(l.settings.Name) } else { l.eLink = l.session.eSession.Receiver(l.settings.Name) } if l.eLink.IsNil() { return l, l.setError(internal.Errorf("cannot create link %s", l)) } l.setSettings() return l, nil }
// Called in proton goroutine func (r *receiver) handleDelivery(delivery proton.Delivery) { // FIXME aconway 2015-09-24: how can this happen if we are remote closed? if r.eLink.State().RemoteClosed() { localClose(r.eLink, r.eLink.RemoteCondition().Error()) return } if delivery.HasMessage() { m, err := delivery.Message() if err != nil { localClose(r.eLink, err) return } internal.Assert(m != nil) r.eLink.Advance() if r.eLink.Credit() < 0 { localClose(r.eLink, internal.Errorf("received message in excess of credit limit")) } else { // We never issue more credit than cap(buffer) so this will not block. r.buffer <- ReceivedMessage{m, delivery, r} } } }
func marshal(v interface{}, data *C.pn_data_t) { switch v := v.(type) { case nil: C.pn_data_put_null(data) case bool: C.pn_data_put_bool(data, C.bool(v)) case int8: C.pn_data_put_byte(data, C.int8_t(v)) case int16: C.pn_data_put_short(data, C.int16_t(v)) case int32: C.pn_data_put_int(data, C.int32_t(v)) case int64: C.pn_data_put_long(data, C.int64_t(v)) case int: if unsafe.Sizeof(0) == 8 { C.pn_data_put_long(data, C.int64_t(v)) } else { C.pn_data_put_int(data, C.int32_t(v)) } case uint8: C.pn_data_put_ubyte(data, C.uint8_t(v)) case uint16: C.pn_data_put_ushort(data, C.uint16_t(v)) case uint32: C.pn_data_put_uint(data, C.uint32_t(v)) case uint64: C.pn_data_put_ulong(data, C.uint64_t(v)) case uint: if unsafe.Sizeof(0) == 8 { C.pn_data_put_ulong(data, C.uint64_t(v)) } else { C.pn_data_put_uint(data, C.uint32_t(v)) } case float32: C.pn_data_put_float(data, C.float(v)) case float64: C.pn_data_put_double(data, C.double(v)) case string: C.pn_data_put_string(data, pnBytes([]byte(v))) case []byte: C.pn_data_put_binary(data, pnBytes(v)) case Binary: C.pn_data_put_binary(data, pnBytes([]byte(v))) case Symbol: C.pn_data_put_symbol(data, pnBytes([]byte(v))) case Map: // Special map type C.pn_data_put_map(data) C.pn_data_enter(data) for key, val := range v { marshal(key, data) marshal(val, data) } C.pn_data_exit(data) default: switch reflect.TypeOf(v).Kind() { case reflect.Map: putMap(data, v) case reflect.Slice: putList(data, v) default: panic(internal.Errorf("cannot marshal %s to AMQP", reflect.TypeOf(v))) } } err := dataError("marshal", data) if err != nil { panic(err) } return }
switch { case n == int(C.PN_OVERFLOW): return buf, overflow case n < 0: return buf, dataError("marshal error", data) default: return buf[:n], nil } } return encodeGrow(buffer, encode) } const minEncode = 256 // overflow is returned when an encoding function can't fit data in the buffer. var overflow = internal.Errorf("buffer too small") // encodeFn encodes into buffer[0:len(buffer)]. // Returns buffer with length adjusted for data encoded. // If buffer too small, returns overflow as error. type encodeFn func(buffer []byte) ([]byte, error) // encodeGrow calls encode() into buffer, if it returns overflow grows the buffer. // Returns the final buffer. func encodeGrow(buffer []byte, encode encodeFn) ([]byte, error) { if buffer == nil || len(buffer) == 0 { buffer = make([]byte, minEncode) } var err error for buffer, err = encode(buffer); err == overflow; buffer, err = encode(buffer) { buffer = make([]byte, 2*len(buffer))
) // Timeout is the error returned if an operation does not complete on time. // // Methods named *Timeout in this package take time.Duration timeout parameter. // // If timeout > 0 and there is no result available before the timeout, they // return a zero or nil value and Timeout as an error. // // If timeout == 0 they will return a result if one is immediatley available or // nil/zero and Timeout as an error if not. // // If timeout == Forever the function will return only when there is a result or // some non-timeout error occurs. // var Timeout = internal.Errorf("timeout") // Forever can be used as a timeout parameter to indicate wait forever. const Forever time.Duration = -1 // timedReceive receives on channel (which can be a chan of any type), waiting // up to timeout. // // timeout==0 means do a non-blocking receive attempt. timeout < 0 means block // forever. Other values mean block up to the timeout. // func timedReceive(channel interface{}, timeout time.Duration) (value interface{}, ok bool, timedout bool) { cases := []reflect.SelectCase{ reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(channel)}, } switch {
func (l *link) setPanicIfOpen() { if l.IsOpen() { panic(internal.Errorf("link is already open %s", l)) } }