func ReceiveMessage(pq *pqueue.PQueue, sqsQuery *urlutils.SQSQuery) sqs_response.SQSResponse { pqCfg := pq.Config() opts := &ReceiveMessageOptions{ WaitTimeSeconds: pqCfg.PopWaitTimeout, VisibilityTimeout: pqCfg.PopLockTimeout, MaxNumberOfMessages: 1, Attributes: nil, MessageAttributes: nil, } paramsLen := len(sqsQuery.ParamsList) - 1 for i := 0; i < paramsLen; i += 2 { err := opts.Parse(sqsQuery.ParamsList[i], sqsQuery.ParamsList[i+1]) if err != nil { return err } } // All messages received from SQS must be locked. res := pq.Pop(opts.VisibilityTimeout, opts.WaitTimeSeconds, opts.MaxNumberOfMessages, true) if res.IsError() { e, _ := res.(error) return sqserr.InvalidParameterValueError(e.Error()) } m, _ := res.(*resp.MessagesResponse) items := m.GetItems() output := &ReceiveMessageResponse{} for _, item := range items { if msgResp := MakeMessageResponse(item, opts, sqsQuery); msgResp != nil { output.Message = append(output.Message, msgResp) } } return output }
// ValidateMessageAttrType makes sure attribute type name is ok. // Validator enforces the same attribute type name limits as AWS SQS. func ValidateMessageAttrType(attrName, v string) *sqserr.SQSError { if len(v) > 255 { return sqserr.InvalidParameterValueError( fmt.Sprintf("Length of message attribute '%s' type must be less than 256 bytes.", attrName)) } if len(v) == 0 { return sqserr.InvalidParameterValueError( fmt.Sprintf("The message attribute '%s' must contain non-empty message attribute type.", attrName)) } typePrefix := strings.SplitN(v, ".", 1)[0] if typePrefix != "String" || typePrefix != "Number" || typePrefix != "Binary" { return sqserr.InvalidParameterValueError( "The message attribute '%s' has an invalid message attribute type, the set of supported type prefixes is Binary, Number, and String.", attrName) } return nil }
// ValidateMessageAttrName makes sure attribute name is ok. // Validator enforces the same attribute name limits as AWS SQS. func ValidateMessageAttrName(v string) *sqserr.SQSError { if len(v) > 255 { return sqserr.InvalidParameterValueError("Length of message attribute name must be less than 256 bytes.") } if len(v) == 0 { return sqserr.InvalidParameterValueError("The request must contain non-empty message attribute name.") } for _, chr := range v { if (chr >= '0' && chr <= '9') || (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || chr == '_' || chr == '-' || chr == '.' { continue } return sqserr.InvalidParameterValueError("Invalid non-alphanumeric character was found in the message attribute name. Can only include alphanumeric characters, hyphens, underscores, or dots.") } return nil }
func (ma *ReqMsgAttr) Parse(paramName string, value string) *sqserr.SQSError { switch paramName { case "Name": ma.Name = value case "Value.DataType": ma.DataType = value case "Value.StringValue": ma.StringValue = value case "Value.BinaryValue": binValue, err := base64.StdEncoding.DecodeString(value) if err != nil { return sqserr.InvalidParameterValueError("Invalid binary data: %s", err.Error()) } ma.BinaryValue = string(binValue) } return nil }
func SetQueueAttributes(pq *pqueue.PQueue, sqsQuery *urlutils.SQSQuery) sqs_response.SQSResponse { attrs, err := urlutils.ParseNNotationAttr("MessageAttribute.", sqsQuery.ParamsList, nil, NewAttribute) if err != nil { return err } attrsLen := len(attrs) params := &pqueue.PQueueParams{} for i := 1; i < attrsLen; i++ { a, ok := attrs[i] if !ok { return sqserr.InvalidParameterValueError("The request must contain non-empty message attribute name.") } attr, _ := a.(*Attribute) err = nil switch attr.Name { case "DelaySeconds": if v, e := strconv.ParseInt(attr.Value, 10, 0); e == nil { v := v * 1000 if v >= 0 && v <= conf.CFG_PQ.MaxDeliveryDelay { params.DeliveryDelay = &v continue } } return sqserr.InvalidAttributeValueError(AttrErrText, attr.Name) case "MaximumMessageSize": if v, e := strconv.ParseInt(attr.Value, 10, 0); e == nil { if v >= 1024 && v <= conf.CFG_PQ.MaxMessageSize { params.MaxMsgSize = &v continue } } return sqserr.InvalidAttributeValueError(AttrErrText, attr.Name) case "MessageRetentionPeriod": if v, e := strconv.ParseInt(attr.Value, 10, 0); e == nil { if v >= 60 && v <= 1209600 { v := v * 1000 params.MsgTTL = &v continue } } return sqserr.InvalidAttributeValueError(AttrErrText, attr.Name) case "ReceiveMessageWaitTimeSeconds": if v, e := strconv.ParseInt(attr.Value, 10, 0); e == nil { v := v * 1000 if v >= 60000 && v <= conf.CFG_PQ.MaxPopWaitTimeout { params.PopWaitTimeout = &v continue } } return sqserr.InvalidAttributeValueError(AttrErrText, attr.Name) case "VisibilityTimeout": if v, e := strconv.ParseInt(attr.Value, 10, 0); e == nil { v := v * 1000 if v > 0 && v <= conf.CFG_PQ.MaxLockTimeout { params.PopLockTimeout = &v continue } } return sqserr.InvalidAttributeValueError(AttrErrText, attr.Name) // These parameters are just ignored. case "Policy": case "ApproximateNumberOfMessages": case "ApproximateNumberOfMessagesDelayed": case "ApproximateNumberOfMessagesNotVisible": case "CreatedTimestamp": case "LastModifiedTimestamp": case "QueueArn": // Any unexpected attribute will produce unexpected attribute error. default: return sqserr.InvalidAttributeNameError("Unknown Attribute: %s", attr.Name) } } pq.SetParams(params) return &SetQueueAttributesResponse{ RequestId: "req", } }
func PushAMessage(pq *pqueue.PQueue, senderId string, paramList []string) sqs_response.SQSResponse { out := &MessageParams{ DelaySeconds: -1, MessageBody: "", } attrs, err := urlutils.ParseNNotationAttr("MessageAttribute.", paramList, out.Parse, NewReqQueueAttr) if err != nil { return err } attrsLen := len(attrs) outAttrs := make(map[string]*sqsmsg.UserAttribute) for i := 1; i <= attrsLen; i++ { a, ok := attrs[i] if !ok { return sqserr.InvalidParameterValueError("The request must contain non-empty message attribute name.") } reqMsgAttr, _ := a.(*ReqMsgAttr) sqs_err := validation.ValidateMessageAttrName(reqMsgAttr.Name) if sqs_err != nil { return sqs_err } sqs_err = validation.ValidateMessageAttrName(reqMsgAttr.DataType) if sqs_err != nil { return sqs_err } if reqMsgAttr.BinaryValue != "" && reqMsgAttr.StringValue != "" { return sqserr.InvalidParameterValueError( "Message attribute name '%s' has multiple values.", reqMsgAttr.Name) } if _, ok := outAttrs[reqMsgAttr.Name]; ok { return sqserr.InvalidParameterValueError( "Message attribute name '%s' already exists.", reqMsgAttr.Name) } if strings.HasPrefix(reqMsgAttr.DataType, "Number") { if _, err := strconv.Atoi(reqMsgAttr.StringValue); err != nil { return sqserr.InvalidParameterValueError( "Could not cast message attribute '%s' value to number.", reqMsgAttr.Name) } } if reqMsgAttr.BinaryValue != "" { if reqMsgAttr.DataType != "Binary" { return sqserr.InvalidParameterValueError( "The message attribute '%s' with type 'Binary' must use field 'Binary'", reqMsgAttr.Name) } outAttrs[reqMsgAttr.Name] = &sqsmsg.UserAttribute{ Type: reqMsgAttr.DataType, Value: reqMsgAttr.BinaryValue, } continue } if reqMsgAttr.StringValue != "" { if reqMsgAttr.DataType != "String" && reqMsgAttr.DataType != "Number" { return sqserr.InvalidParameterValueError( "The message attribute '%s' with type 'String' must use field 'String'", reqMsgAttr.Name) } outAttrs[reqMsgAttr.Name] = &sqsmsg.UserAttribute{ Type: reqMsgAttr.DataType, Value: reqMsgAttr.StringValue, } } } msgId := IdGen.RandId() if out.DelaySeconds < 0 { out.DelaySeconds = pq.Config().DeliveryDelay } else if out.DelaySeconds > conf.CFG_PQ.MaxDeliveryDelay { return sqserr.InvalidParameterValueError( "Delay secods must be between 0 and %d", conf.CFG_PQ.MaxDeliveryDelay/1000) } bodyMd5str := fmt.Sprintf("%x", md5.Sum(enc.UnsafeStringToBytes(out.MessageBody))) attrMd5 := CalcAttrMd5(outAttrs) msgPayload := sqsmsg.SQSMessagePayload{ UserAttributes: outAttrs, MD5OfMessageBody: bodyMd5str, MD5OfMessageAttributes: attrMd5, SenderId: senderId, SentTimestamp: strconv.FormatInt(utils.Uts(), 10), Payload: out.MessageBody, } d, marshalErr := msgPayload.Marshal() if marshalErr != nil { log.Error("Failed to serialize message payload: %v", err) } payload := enc.UnsafeBytesToString(d) resp := pq.Push(msgId, payload, pq.Config().MsgTtl, out.DelaySeconds, 1) if resp.IsError() { e, _ := resp.(error) return sqserr.InvalidParameterValueError(e.Error()) } return &SendMessageResponse{ MessageId: msgId, MD5OfMessageBody: bodyMd5str, MD5OfMessageAttributes: attrMd5, RequestId: "req", } }