package client import ( "regexp" "sort" "strconv" "strings" "github.com/go-stomp/stomp/v3" "github.com/go-stomp/stomp/v3/frame" ) const ( // Maximum permitted heart-beat timeout, about 11.5 days. // Any client CONNECT frame with a larger value than this // will be rejected. maxHeartBeat = 999999999 ) var ( // Regexp for heart-beat header value heartBeatRegexp = regexp.MustCompile("^[0-9]{1,9},[0-9]{1,9}$") ) // Determine the most acceptable version based on the accept-version // header of a CONNECT or STOMP frame. // // Returns stomp.V10 if a CONNECT frame and the accept-version header is missing. // // Returns an error if the frame is not a CONNECT or STOMP frame, or // if the accept-header is malformed or does not contain any compatible // version numbers. Also returns an error if the accept-header is missing // for a STOMP frame. // // Otherwise, returns the highest compatible version specified in the // accept-version header. Compatible versions are V1_0, V1_1 and V1_2. func determineVersion(f *frame.Frame) (version stomp.Version, err error) { // frame can be CONNECT or STOMP with slightly different // handling of accept-verion for each isConnect := f.Command == frame.CONNECT if !isConnect && f.Command != frame.STOMP { err = notConnectFrame return } // start with an error, and remove if successful err = unknownVersion if acceptVersion, ok := f.Header.Contains(frame.AcceptVersion); ok { // sort the versions so that the latest version comes last versions := strings.Split(acceptVersion, ",") sort.Strings(versions) for _, v := range versions { switch stomp.Version(v) { case stomp.V10: version = stomp.V10 err = nil case stomp.V11: version = stomp.V11 err = nil case stomp.V12: version = stomp.V12 err = nil } } } else { // CONNECT frames can be missing the accept-version header, // we assume V1.0 in this case. STOMP frames were introduced // in V1.1, so they must have an accept-version header. if isConnect { // no "accept-version" header, so we assume 1.0 version = stomp.V10 err = nil } else { err = missingHeader(frame.AcceptVersion) } } return } // Determine the heart-beat values in a CONNECT or STOMP frame. // // Returns 0,0 if the heart-beat header is missing. Otherwise // returns the cx and cy values in the frame. // // Returns an error if the heart-beat header is malformed, or if // the frame is not a CONNECT or STOMP frame. In this implementation, // a heart-beat header is considered malformed if either cx or cy // is greater than MaxHeartBeat. func getHeartBeat(f *frame.Frame) (cx, cy int, err error) { if f.Command != frame.CONNECT && f.Command != frame.STOMP && f.Command != frame.CONNECTED { err = invalidOperationForFrame return } if heartBeat, ok := f.Header.Contains(frame.HeartBeat); ok { if !heartBeatRegexp.MatchString(heartBeat) { err = invalidHeartBeat return } // no error checking here because we are confident // that everything will work because the regexp matches. slice := strings.Split(heartBeat, ",") value1, _ := strconv.ParseUint(slice[0], 10, 32) value2, _ := strconv.ParseUint(slice[1], 10, 32) cx = int(value1) cy = int(value2) } else { // heart-beat header not present // this else clause is not necessary, but // included for clarity. cx = 0 cy = 0 } return }