No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 

586 líneas
15 KiB

  1. //
  2. // gosqs - Go packages to interact with the Amazon SQS Web Services.
  3. //
  4. // depends on https://wiki.ubuntu.com/goamz
  5. //
  6. //
  7. // Written by Prudhvi Krishna Surapaneni <me@prudhvi.net>
  8. // Extended by Fabrizio Milo <mistobaan@gmail.com>
  9. //
  10. package sqs
  11. import (
  12. "encoding/xml"
  13. "errors"
  14. "fmt"
  15. "io"
  16. "io/ioutil"
  17. "log"
  18. "net/http"
  19. "net/http/httputil"
  20. "net/url"
  21. "strconv"
  22. "strings"
  23. "time"
  24. "github.com/goamz/goamz/aws"
  25. )
  26. const API_VERSION = "2012-11-05"
  27. const debug = false
  28. // The SQS type encapsulates operation with an SQS region.
  29. type SQS struct {
  30. aws.Auth
  31. aws.Region
  32. private byte // Reserve the right of using private data.
  33. transport *http.Transport
  34. }
  35. // NewFrom Create A new SQS Client given an access and secret Key
  36. // region must be one of "us.east, us.west, eu.west"
  37. func NewFrom(accessKey, secretKey, region string) (*SQS, error) {
  38. auth := aws.Auth{AccessKey: accessKey, SecretKey: secretKey}
  39. aws_region := aws.USEast
  40. switch region {
  41. case "us.east", "us.east.1":
  42. aws_region = aws.USEast
  43. case "us.east.2":
  44. aws_region = aws.USEast2
  45. case "us.west", "us.west.1":
  46. aws_region = aws.USWest
  47. case "us.west.2":
  48. aws_region = aws.USWest2
  49. case "eu.west":
  50. aws_region = aws.EUWest
  51. case "ap.southeast", "ap.southeast.1":
  52. aws_region = aws.APSoutheast
  53. case "ap.southeast.2":
  54. aws_region = aws.APSoutheast2
  55. case "ap.northeast", "ap.northeast.1":
  56. aws_region = aws.APNortheast
  57. case "ap.northeast.2":
  58. aws_region = aws.APNortheast2
  59. case "sa.east", "sa.east.1":
  60. aws_region = aws.SAEast
  61. case "cn.north", "cn.north.1":
  62. aws_region = aws.CNNorth
  63. default:
  64. return nil, errors.New(fmt.Sprintf("Unknown/Unsupported region %s", region))
  65. }
  66. aws_sqs := New(auth, aws_region)
  67. return aws_sqs, nil
  68. }
  69. // NewFrom Create A new SQS Client from an exisisting aws.Auth
  70. func New(auth aws.Auth, region aws.Region) *SQS {
  71. return &SQS{auth, region, 0, nil}
  72. }
  73. // NewFromTransport Create A new SQS Client that uses a given &http.Transport
  74. func NewFromTransport(auth aws.Auth, region aws.Region, transport *http.Transport) *SQS {
  75. return &SQS{auth, region, 0, transport}
  76. }
  77. // Queue Reference to a Queue
  78. type Queue struct {
  79. *SQS
  80. Url string
  81. }
  82. type CreateQueueResponse struct {
  83. QueueUrl string `xml:"CreateQueueResult>QueueUrl"`
  84. ResponseMetadata ResponseMetadata
  85. }
  86. type GetQueueUrlResponse struct {
  87. QueueUrl string `xml:"GetQueueUrlResult>QueueUrl"`
  88. ResponseMetadata ResponseMetadata
  89. }
  90. type ListQueuesResponse struct {
  91. QueueUrl []string `xml:"ListQueuesResult>QueueUrl"`
  92. ResponseMetadata ResponseMetadata
  93. }
  94. type DeleteMessageResponse struct {
  95. ResponseMetadata ResponseMetadata
  96. }
  97. type DeleteQueueResponse struct {
  98. ResponseMetadata ResponseMetadata
  99. }
  100. type PurgeQueueResponse struct {
  101. ResponseMetadata ResponseMetadata
  102. }
  103. type SendMessageResponse struct {
  104. MD5 string `xml:"SendMessageResult>MD5OfMessageBody"`
  105. MD5OfMessageAttributes string `xml:"SendMessageResult>MD5OfMessageAttributes"`
  106. Id string `xml:"SendMessageResult>MessageId"`
  107. ResponseMetadata ResponseMetadata
  108. }
  109. type ReceiveMessageResponse struct {
  110. Messages []Message `xml:"ReceiveMessageResult>Message"`
  111. ResponseMetadata ResponseMetadata
  112. }
  113. type Message struct {
  114. MessageId string `xml:"MessageId"`
  115. Body string `xml:"Body"`
  116. MD5OfBody string `xml:"MD5OfBody"`
  117. ReceiptHandle string `xml:"ReceiptHandle"`
  118. Attribute []Attribute `xml:"Attribute"`
  119. MessageAttribute []MessageAttribute `xml:"MessageAttribute"`
  120. MD5OfMessageAttributes string `xml:"MD5OfMessageAttributes"`
  121. }
  122. type Attribute struct {
  123. Name string `xml:"Name"`
  124. Value string `xml:"Value"`
  125. }
  126. type MessageAttribute struct {
  127. Name string `xml:"Name"`
  128. Value MessageAttributeValue `xml:"Value"`
  129. }
  130. type MessageAttributeValue struct {
  131. DataType string `xml:"DataType"`
  132. BinaryValue []byte `xml:"BinaryValue"`
  133. StringValue string `xml:"StringValue"`
  134. // Not yet implemented (Reserved for future use)
  135. BinaryListValues [][]byte `xml:"BinaryListValues"`
  136. StringListValues []string `xml:"StringListValues"`
  137. }
  138. type ChangeMessageVisibilityResponse struct {
  139. ResponseMetadata ResponseMetadata
  140. }
  141. type GetQueueAttributesResponse struct {
  142. Attributes []Attribute `xml:"GetQueueAttributesResult>Attribute"`
  143. ResponseMetadata ResponseMetadata
  144. }
  145. type ResponseMetadata struct {
  146. RequestId string
  147. BoxUsage float64
  148. }
  149. type Error struct {
  150. StatusCode int
  151. Code string
  152. Message string
  153. RequestId string
  154. }
  155. func (err *Error) Error() string {
  156. if err.Code == "" {
  157. return err.Message
  158. }
  159. return fmt.Sprintf("%s (%s)", err.Message, err.Code)
  160. }
  161. func (err *Error) String() string {
  162. return err.Message
  163. }
  164. type xmlErrors struct {
  165. RequestId string
  166. Errors []Error `xml:"Errors>Error"`
  167. Error Error
  168. }
  169. // CreateQueue create a queue with a specific name
  170. func (s *SQS) CreateQueue(queueName string) (*Queue, error) {
  171. return s.CreateQueueWithTimeout(queueName, 30)
  172. }
  173. // CreateQueue create a queue with a specific name and a timeout
  174. func (s *SQS) CreateQueueWithTimeout(queueName string, timeout int) (*Queue, error) {
  175. params := map[string]string{
  176. "VisibilityTimeout": strconv.Itoa(timeout),
  177. }
  178. return s.CreateQueueWithAttributes(queueName, params)
  179. }
  180. func (s *SQS) CreateQueueWithAttributes(queueName string, attrs map[string]string) (q *Queue, err error) {
  181. resp, err := s.newQueue(queueName, attrs)
  182. if err != nil {
  183. return nil, err
  184. }
  185. q = &Queue{s, resp.QueueUrl}
  186. return
  187. }
  188. // GetQueue get a reference to the given quename
  189. func (s *SQS) GetQueue(queueName string) (*Queue, error) {
  190. var q *Queue
  191. resp, err := s.getQueueUrl(queueName)
  192. if err != nil {
  193. return q, err
  194. }
  195. q = &Queue{s, resp.QueueUrl}
  196. return q, nil
  197. }
  198. func (s *SQS) QueueFromArn(queueUrl string) (q *Queue) {
  199. q = &Queue{s, queueUrl}
  200. return
  201. }
  202. func (s *SQS) getQueueUrl(queueName string) (resp *GetQueueUrlResponse, err error) {
  203. resp = &GetQueueUrlResponse{}
  204. params := makeParams("GetQueueUrl")
  205. params["QueueName"] = queueName
  206. err = s.query("", params, resp)
  207. return resp, err
  208. }
  209. func (s *SQS) newQueue(queueName string, attrs map[string]string) (resp *CreateQueueResponse, err error) {
  210. resp = &CreateQueueResponse{}
  211. params := makeParams("CreateQueue")
  212. params["QueueName"] = queueName
  213. i := 1
  214. for k, v := range attrs {
  215. nameParam := fmt.Sprintf("Attribute.%d.Name", i)
  216. valParam := fmt.Sprintf("Attribute.%d.Value", i)
  217. params[nameParam] = k
  218. params[valParam] = v
  219. i++
  220. }
  221. err = s.query("", params, resp)
  222. return
  223. }
  224. func (s *SQS) ListQueues(QueueNamePrefix string) (resp *ListQueuesResponse, err error) {
  225. resp = &ListQueuesResponse{}
  226. params := makeParams("ListQueues")
  227. if QueueNamePrefix != "" {
  228. params["QueueNamePrefix"] = QueueNamePrefix
  229. }
  230. err = s.query("", params, resp)
  231. return
  232. }
  233. func (q *Queue) Delete() (resp *DeleteQueueResponse, err error) {
  234. resp = &DeleteQueueResponse{}
  235. params := makeParams("DeleteQueue")
  236. err = q.SQS.query(q.Url, params, resp)
  237. return
  238. }
  239. func (q *Queue) Purge() (resp *PurgeQueueResponse, err error) {
  240. resp = &PurgeQueueResponse{}
  241. params := makeParams("PurgeQueue")
  242. err = q.SQS.query(q.Url, params, resp)
  243. return
  244. }
  245. func (q *Queue) SendMessageWithDelay(MessageBody string, DelaySeconds int64) (resp *SendMessageResponse, err error) {
  246. resp = &SendMessageResponse{}
  247. params := makeParams("SendMessage")
  248. params["MessageBody"] = MessageBody
  249. params["DelaySeconds"] = strconv.Itoa(int(DelaySeconds))
  250. err = q.SQS.query(q.Url, params, resp)
  251. return
  252. }
  253. func (q *Queue) SendMessage(MessageBody string) (resp *SendMessageResponse, err error) {
  254. resp = &SendMessageResponse{}
  255. params := makeParams("SendMessage")
  256. params["MessageBody"] = MessageBody
  257. err = q.SQS.query(q.Url, params, resp)
  258. return
  259. }
  260. func (q *Queue) SendMessageWithAttributes(MessageBody string, attrs map[string]string) (resp *SendMessageResponse, err error) {
  261. resp = &SendMessageResponse{}
  262. params := makeParams("SendMessage")
  263. params["MessageBody"] = MessageBody
  264. i := 1
  265. for k, v := range attrs {
  266. nameParam := fmt.Sprintf("MessageAttribute.%d.Name", i)
  267. valParam := fmt.Sprintf("MessageAttribute.%d.Value.StringValue", i)
  268. typeParam := fmt.Sprintf("MessageAttribute.%d.Value.DataType", i)
  269. params[nameParam] = k
  270. params[valParam] = v
  271. params[typeParam] = "String"
  272. i++
  273. }
  274. err = q.SQS.query(q.Url, params, resp)
  275. return
  276. }
  277. // ReceiveMessageWithVisibilityTimeout
  278. func (q *Queue) ReceiveMessageWithVisibilityTimeout(MaxNumberOfMessages, VisibilityTimeoutSec int) (*ReceiveMessageResponse, error) {
  279. params := map[string]string{
  280. "MaxNumberOfMessages": strconv.Itoa(MaxNumberOfMessages),
  281. "VisibilityTimeout": strconv.Itoa(VisibilityTimeoutSec),
  282. }
  283. return q.ReceiveMessageWithParameters(params)
  284. }
  285. // ReceiveMessage
  286. func (q *Queue) ReceiveMessage(MaxNumberOfMessages int) (*ReceiveMessageResponse, error) {
  287. params := map[string]string{
  288. "MaxNumberOfMessages": strconv.Itoa(MaxNumberOfMessages),
  289. }
  290. return q.ReceiveMessageWithParameters(params)
  291. }
  292. func (q *Queue) ReceiveMessageWithParameters(p map[string]string) (resp *ReceiveMessageResponse, err error) {
  293. resp = &ReceiveMessageResponse{}
  294. params := makeParams("ReceiveMessage")
  295. params["AttributeName"] = "All"
  296. params["MessageAttributeNames"] = "All"
  297. for k, v := range p {
  298. params[k] = v
  299. }
  300. err = q.SQS.query(q.Url, params, resp)
  301. return
  302. }
  303. func (q *Queue) ChangeMessageVisibility(M *Message, VisibilityTimeout int) (resp *ChangeMessageVisibilityResponse, err error) {
  304. resp = &ChangeMessageVisibilityResponse{}
  305. params := makeParams("ChangeMessageVisibility")
  306. params["VisibilityTimeout"] = strconv.Itoa(VisibilityTimeout)
  307. params["ReceiptHandle"] = M.ReceiptHandle
  308. err = q.SQS.query(q.Url, params, resp)
  309. return
  310. }
  311. func (q *Queue) GetQueueAttributes(A string) (resp *GetQueueAttributesResponse, err error) {
  312. resp = &GetQueueAttributesResponse{}
  313. params := makeParams("GetQueueAttributes")
  314. params["AttributeName"] = A
  315. err = q.SQS.query(q.Url, params, resp)
  316. return
  317. }
  318. func (q *Queue) DeleteMessage(M *Message) (resp *DeleteMessageResponse, err error) {
  319. return q.DeleteMessageUsingReceiptHandle(M.ReceiptHandle)
  320. }
  321. func (q *Queue) DeleteMessageUsingReceiptHandle(receiptHandle string) (resp *DeleteMessageResponse, err error) {
  322. resp = &DeleteMessageResponse{}
  323. params := makeParams("DeleteMessage")
  324. params["ReceiptHandle"] = receiptHandle
  325. err = q.SQS.query(q.Url, params, resp)
  326. return
  327. }
  328. type SendMessageBatchResultEntry struct {
  329. Id string `xml:"Id"`
  330. MessageId string `xml:"MessageId"`
  331. MD5OfMessageBody string `xml:"MD5OfMessageBody"`
  332. }
  333. type SendMessageBatchResponse struct {
  334. SendMessageBatchResult []SendMessageBatchResultEntry `xml:"SendMessageBatchResult>SendMessageBatchResultEntry"`
  335. ResponseMetadata ResponseMetadata
  336. }
  337. /* SendMessageBatch
  338. */
  339. func (q *Queue) SendMessageBatch(msgList []Message) (resp *SendMessageBatchResponse, err error) {
  340. resp = &SendMessageBatchResponse{}
  341. params := makeParams("SendMessageBatch")
  342. for idx, msg := range msgList {
  343. count := idx + 1
  344. params[fmt.Sprintf("SendMessageBatchRequestEntry.%d.Id", count)] = fmt.Sprintf("msg-%d", count)
  345. params[fmt.Sprintf("SendMessageBatchRequestEntry.%d.MessageBody", count)] = msg.Body
  346. }
  347. err = q.SQS.query(q.Url, params, resp)
  348. return
  349. }
  350. /* SendMessageBatchString
  351. */
  352. func (q *Queue) SendMessageBatchString(msgList []string) (resp *SendMessageBatchResponse, err error) {
  353. resp = &SendMessageBatchResponse{}
  354. params := makeParams("SendMessageBatch")
  355. for idx, msg := range msgList {
  356. count := idx + 1
  357. params[fmt.Sprintf("SendMessageBatchRequestEntry.%d.Id", count)] = fmt.Sprintf("msg-%d", count)
  358. params[fmt.Sprintf("SendMessageBatchRequestEntry.%d.MessageBody", count)] = msg
  359. }
  360. err = q.SQS.query(q.Url, params, resp)
  361. return
  362. }
  363. type DeleteMessageBatchResponse struct {
  364. DeleteMessageBatchResult []struct {
  365. Id string
  366. SenderFault bool
  367. Code string
  368. Message string
  369. } `xml:"DeleteMessageBatchResult>DeleteMessageBatchResultEntry"`
  370. ResponseMetadata ResponseMetadata
  371. }
  372. /* DeleteMessageBatch */
  373. func (q *Queue) DeleteMessageBatch(msgList []Message) (resp *DeleteMessageBatchResponse, err error) {
  374. resp = &DeleteMessageBatchResponse{}
  375. params := makeParams("DeleteMessageBatch")
  376. lutMsg := make(map[string]Message)
  377. for idx := range msgList {
  378. params[fmt.Sprintf("DeleteMessageBatchRequestEntry.%d.Id", idx+1)] = msgList[idx].MessageId
  379. params[fmt.Sprintf("DeleteMessageBatchRequestEntry.%d.ReceiptHandle", idx+1)] = msgList[idx].ReceiptHandle
  380. lutMsg[string(msgList[idx].MessageId)] = msgList[idx]
  381. }
  382. err = q.SQS.query(q.Url, params, resp)
  383. messageWithErrors := make([]Message, 0, len(msgList))
  384. for idx := range resp.DeleteMessageBatchResult {
  385. if resp.DeleteMessageBatchResult[idx].SenderFault {
  386. msg, ok := lutMsg[resp.DeleteMessageBatchResult[idx].Id]
  387. if ok {
  388. messageWithErrors = append(messageWithErrors, msg)
  389. }
  390. }
  391. }
  392. if len(messageWithErrors) > 0 {
  393. log.Printf("%d Message have not been sent", len(messageWithErrors))
  394. }
  395. return
  396. }
  397. func (s *SQS) query(queueUrl string, params map[string]string, resp interface{}) (err error) {
  398. params["Version"] = API_VERSION
  399. params["Timestamp"] = time.Now().In(time.UTC).Format(time.RFC3339)
  400. var url_ *url.URL
  401. switch {
  402. // fully qualified queueUrl
  403. case strings.HasPrefix(queueUrl, "http"):
  404. url_, err = url.Parse(queueUrl)
  405. // relative queueUrl
  406. case strings.HasPrefix(queueUrl, "/"):
  407. url_, err = url.Parse(s.Region.SQSEndpoint + queueUrl)
  408. // zero-value for queueUrl
  409. default:
  410. url_, err = url.Parse(s.Region.SQSEndpoint)
  411. }
  412. if err != nil {
  413. return err
  414. }
  415. if s.Auth.Token() != "" {
  416. params["SecurityToken"] = s.Auth.Token()
  417. }
  418. var r *http.Response
  419. var sarray []string
  420. for k, v := range params {
  421. sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(v))
  422. }
  423. req, err := http.NewRequest("GET", fmt.Sprintf("%s?%s", url_, strings.Join(sarray, "&")), nil)
  424. if err != nil {
  425. return err
  426. }
  427. signer := aws.NewV4Signer(s.Auth, "sqs", s.Region)
  428. signer.Sign(req)
  429. var client http.Client
  430. if s.transport == nil {
  431. client = http.Client{}
  432. } else {
  433. client = http.Client{Transport: s.transport}
  434. }
  435. r, err = client.Do(req)
  436. if debug {
  437. log.Printf("GET %s\n", url_.String())
  438. }
  439. if err != nil {
  440. return err
  441. }
  442. defer r.Body.Close()
  443. if debug {
  444. dump, _ := httputil.DumpResponse(r, true)
  445. log.Printf("DUMP:%s\n", string(dump))
  446. }
  447. if r.StatusCode != 200 {
  448. return buildError(r)
  449. }
  450. err = xml.NewDecoder(r.Body).Decode(resp)
  451. io.Copy(ioutil.Discard, r.Body)
  452. return err
  453. }
  454. func buildError(r *http.Response) error {
  455. errors := xmlErrors{}
  456. xml.NewDecoder(r.Body).Decode(&errors)
  457. var err Error
  458. if len(errors.Errors) > 0 {
  459. err = errors.Errors[0]
  460. } else {
  461. err = errors.Error
  462. }
  463. err.RequestId = errors.RequestId
  464. err.StatusCode = r.StatusCode
  465. if err.Message == "" {
  466. err.Message = r.Status
  467. }
  468. return &err
  469. }
  470. func makeParams(action string) map[string]string {
  471. params := make(map[string]string)
  472. params["Action"] = action
  473. return params
  474. }
  475. func multimap(p map[string]string) url.Values {
  476. q := make(url.Values, len(p))
  477. for k, v := range p {
  478. q[k] = []string{v}
  479. }
  480. return q
  481. }