fix: v0.1.19 — валидация VisibilityTimeout/WaitTimeSeconds/пустой MessageBody, fix hardcore_test LONG_Q length
This commit is contained in:
@@ -3,85 +3,86 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func ChangeMessageVisibilityV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewChangeMessageVisibilityRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - ChangeMessageVisibilityV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
requestBody := models.NewChangeMessageVisibilityRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - ChangeMessageVisibilityV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
t := getTenantFromContext(req)
|
||||
if t == nil {
|
||||
return utils.CreateErrorResponseV1("InvalidClientTokenId", true)
|
||||
}
|
||||
t := getTenantFromContext(req)
|
||||
if t == nil {
|
||||
return utils.CreateErrorResponseV1("InvalidClientTokenId", true)
|
||||
}
|
||||
|
||||
vars := mux.Vars(req)
|
||||
queueUrl := requestBody.QueueUrl
|
||||
queueName := ""
|
||||
if queueUrl == "" {
|
||||
queueName = vars["queueName"]
|
||||
} else {
|
||||
uriSegments := strings.Split(queueUrl, "/")
|
||||
queueName = uriSegments[len(uriSegments)-1]
|
||||
}
|
||||
vars := mux.Vars(req)
|
||||
queueUrl := requestBody.QueueUrl
|
||||
queueName := ""
|
||||
if queueUrl == "" {
|
||||
queueName = vars["queueName"]
|
||||
} else {
|
||||
uriSegments := strings.Split(queueUrl, "/")
|
||||
queueName = uriSegments[len(uriSegments)-1]
|
||||
}
|
||||
|
||||
key := tenantQueueKey(t.AccessKey, queueName)
|
||||
receiptHandle := requestBody.ReceiptHandle
|
||||
visibilityTimeout := requestBody.VisibilityTimeout
|
||||
key := tenantQueueKey(t.AccessKey, queueName)
|
||||
receiptHandle := requestBody.ReceiptHandle
|
||||
visibilityTimeout := requestBody.VisibilityTimeout
|
||||
|
||||
if visibilityTimeout > 43200 {
|
||||
return utils.CreateErrorResponseV1("ValidationError", true)
|
||||
}
|
||||
if visibilityTimeout < 0 || visibilityTimeout > 43200 {
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
if _, ok := models.SyncQueues.Queues[key]; !ok {
|
||||
return utils.CreateErrorResponseV1("QueueNotFound", true)
|
||||
}
|
||||
if _, ok := models.SyncQueues.Queues[key]; !ok {
|
||||
return utils.CreateErrorResponseV1("QueueNotFound", true)
|
||||
}
|
||||
|
||||
models.SyncQueues.Lock()
|
||||
messageFound := false
|
||||
for i := 0; i < len(models.SyncQueues.Queues[key].Messages); i++ {
|
||||
queue := models.SyncQueues.Queues[key]
|
||||
msgs := queue.Messages
|
||||
if msgs[i].ReceiptHandle == receiptHandle {
|
||||
timeout := models.SyncQueues.Queues[key].VisibilityTimeout
|
||||
if visibilityTimeout == 0 {
|
||||
msgs[i].ReceiptTime = time.Now().UTC()
|
||||
msgs[i].ReceiptHandle = ""
|
||||
msgs[i].VisibilityTimeout = time.Now().Add(time.Duration(timeout) * time.Second)
|
||||
msgs[i].Retry++
|
||||
if queue.MaxReceiveCount > 0 &&
|
||||
queue.DeadLetterQueue != nil &&
|
||||
msgs[i].Retry >= queue.MaxReceiveCount {
|
||||
queue.DeadLetterQueue.Messages = append(queue.DeadLetterQueue.Messages, msgs[i])
|
||||
queue.Messages = append(queue.Messages[:i], queue.Messages[i+1:]...)
|
||||
}
|
||||
} else {
|
||||
msgs[i].VisibilityTimeout = time.Now().Add(time.Duration(visibilityTimeout) * time.Second)
|
||||
}
|
||||
messageFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
models.SyncQueues.Unlock()
|
||||
if !messageFound {
|
||||
return utils.CreateErrorResponseV1("MessageNotInFlight", true)
|
||||
}
|
||||
models.SyncQueues.Lock()
|
||||
messageFound := false
|
||||
for i := 0; i < len(models.SyncQueues.Queues[key].Messages); i++ {
|
||||
queue := models.SyncQueues.Queues[key]
|
||||
msgs := queue.Messages
|
||||
if msgs[i].ReceiptHandle == receiptHandle {
|
||||
timeout := models.SyncQueues.Queues[key].VisibilityTimeout
|
||||
if visibilityTimeout == 0 {
|
||||
msgs[i].ReceiptTime = time.Now().UTC()
|
||||
msgs[i].ReceiptHandle = ""
|
||||
msgs[i].VisibilityTimeout = time.Now().Add(time.Duration(timeout) * time.Second)
|
||||
msgs[i].Retry++
|
||||
if queue.MaxReceiveCount > 0 &&
|
||||
queue.DeadLetterQueue != nil &&
|
||||
msgs[i].Retry >= queue.MaxReceiveCount {
|
||||
queue.DeadLetterQueue.Messages = append(queue.DeadLetterQueue.Messages, msgs[i])
|
||||
queue.Messages = append(queue.Messages[:i], queue.Messages[i+1:]...)
|
||||
}
|
||||
} else {
|
||||
msgs[i].VisibilityTimeout = time.Now().Add(time.Duration(visibilityTimeout) * time.Second)
|
||||
}
|
||||
messageFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
models.SyncQueues.Unlock()
|
||||
if !messageFound {
|
||||
return utils.CreateErrorResponseV1("MessageNotInFlight", true)
|
||||
}
|
||||
|
||||
respStruct := models.ChangeMessageVisibilityResult{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
return http.StatusOK, &respStruct
|
||||
respStruct := models.ChangeMessageVisibilityResult{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
return http.StatusOK, &respStruct
|
||||
}
|
||||
|
||||
@@ -57,14 +57,21 @@ func ReceiveMessageV1(req *http.Request) (int, interfaces.AbstractResponseBody)
|
||||
var messages []*models.ResultMessage
|
||||
respStruct := models.ReceiveMessageResponse{}
|
||||
|
||||
// Валидация VisibilityTimeout: AWS SQS допускает 0–43200
|
||||
if requestBody.VisibilityTimeout < 0 || requestBody.VisibilityTimeout > MaxVisibilityTimeout {
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
waitTimeSeconds := requestBody.WaitTimeSeconds
|
||||
if waitTimeSeconds == 0 {
|
||||
models.SyncQueues.RLock()
|
||||
waitTimeSeconds = models.SyncQueues.Queues[key].ReceiveMessageWaitTimeSeconds
|
||||
models.SyncQueues.RUnlock()
|
||||
}
|
||||
// Fix #4: clamp WaitTimeSeconds к AWS лимиту 0–20
|
||||
waitTimeSeconds = ClampInt(waitTimeSeconds, 0, MaxReceiveMessageWaitTimeSeconds)
|
||||
// Валидация WaitTimeSeconds: AWS SQS допускает 0–20, иначе ошибка
|
||||
if waitTimeSeconds < 0 || waitTimeSeconds > MaxReceiveMessageWaitTimeSeconds {
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
if waitTimeSeconds > 0 {
|
||||
deadline := time.Now().Add(time.Duration(waitTimeSeconds) * time.Second)
|
||||
|
||||
@@ -35,6 +35,9 @@ func SendMessageV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
}
|
||||
|
||||
messageBody := requestBody.MessageBody
|
||||
if messageBody == "" {
|
||||
return utils.CreateErrorResponseV1("MissingParameter", true)
|
||||
}
|
||||
messageGroupID := requestBody.MessageGroupId
|
||||
messageDeduplicationID := requestBody.MessageDeduplicationId
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ func init() {
|
||||
"ValidationError": {HttpError: http.StatusBadRequest, Type: "ValidationError", Code: "AWS.SimpleQueueService.ValidationError", Message: "The input fails to satisfy the constraints specified by an AWS service."},
|
||||
// LimitExceeded — превышен лимит очередей тенанта (max_queues)
|
||||
"LimitExceeded": {HttpError: http.StatusBadRequest, Type: "LimitExceeded", Code: "AWS.SimpleQueueService.LimitExceeded", Message: "You've reached the limit on the number of queues."},
|
||||
// MissingParameter — обязательный параметр отсутствует (например, пустой MessageBody)
|
||||
"MissingParameter": {HttpError: http.StatusBadRequest, Type: "MissingParameter", Code: "MissingParameter", Message: "The request must contain the parameter MessageBody."},
|
||||
}
|
||||
SnsErrors = map[string]SnsErrorType{
|
||||
"InvalidParameterValue": {HttpError: http.StatusBadRequest, Type: "InvalidParameterValue", Code: "AWS.SimpleNotificationService.InvalidParameterValue", Message: "An invalid or out-of-range value was supplied for the input parameter."},
|
||||
|
||||
@@ -738,14 +738,15 @@ echo "── 15. Граничные случаи: имена, пустые те
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
# Очень длинное имя очереди (AWS позволяет до 80 символов)
|
||||
LONG_Q=$(printf 'q%.0s' {1..70})"-${TS}"
|
||||
# 69 q's + "-" + 10 цифр TS = 80 символов (ровно лимит)
|
||||
LONG_Q=$(printf 'q%.0s' {1..69})"-${TS}"
|
||||
LONG_QURL=$(qurl "$T1_ID" "$LONG_Q")
|
||||
R=$(sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$LONG_Q" 2>&1)
|
||||
check "Граница: длинное имя очереди (70 сим)" "$R" "Queue"
|
||||
check "Граница: длинное имя очереди (80 сим)" "$R" "Queue"
|
||||
|
||||
# Пустое тело сообщения
|
||||
# Пустое тело сообщения — AWS SQS требует непустой MessageBody
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message --queue-url "$LONG_QURL" --message-body "" 2>&1)
|
||||
check "Граница: пустое тело сообщения" "$R" "MessageId"
|
||||
check "Граница: пустое тело сообщения → ошибка" "$R" "ERROR|Error|error|MissingParameter|Invalid"
|
||||
|
||||
# Receive из пустой очереди
|
||||
sqs "$T1_AK" "$T1_SK" purge-queue --queue-url "$LONG_QURL" >/dev/null 2>&1 || true
|
||||
|
||||
Reference in New Issue
Block a user