gopkg.in/rethinkdb/rethinkdb-go.v6@v6.2.2/connection_test.go (about) 1 package rethinkdb 2 3 import ( 4 "encoding/binary" 5 "encoding/json" 6 "github.com/opentracing/opentracing-go" 7 "github.com/opentracing/opentracing-go/mocktracer" 8 "github.com/stretchr/testify/mock" 9 "golang.org/x/net/context" 10 test "gopkg.in/check.v1" 11 p "gopkg.in/rethinkdb/rethinkdb-go.v6/ql2" 12 "io" 13 "sync" 14 "time" 15 ) 16 17 func runConnection(c *Connection) <-chan struct{} { 18 wg := &sync.WaitGroup{} 19 wg.Add(2) 20 go func() { 21 c.readSocket() 22 wg.Done() 23 }() 24 go func() { 25 c.processResponses() 26 wg.Done() 27 }() 28 29 doneChan := make(chan struct{}) 30 go func() { 31 wg.Wait() 32 close(doneChan) 33 }() 34 return doneChan 35 } 36 37 type ConnectionSuite struct{} 38 39 var _ = test.Suite(&ConnectionSuite{}) 40 41 func (s *ConnectionSuite) TestConnection_Query_Ok(c *test.C) { 42 ctx := context.Background() 43 token := int64(1) 44 q := testQuery(DB("db").Table("table").Get("id")) 45 writeData := serializeQuery(token, q) 46 respData := serializeAtomResponse() 47 header := respHeader(token, respData) 48 49 conn := &connMock{} 50 conn.On("Write", writeData).Return(len(writeData), nil, nil) 51 conn.On("Read", respHeaderLen).Return(header, respHeaderLen, nil, nil) 52 conn.On("Read", len(respData)).Return(respData, len(respData), nil, nil) 53 conn.On("Close").Return(nil) 54 55 connection := newConnection(conn, "addr", &ConnectOpts{}) 56 closed := runConnection(connection) 57 response, cursor, err := connection.Query(ctx, q) 58 connection.Close() 59 <-closed 60 61 c.Assert(response, test.NotNil) 62 c.Assert(response.Token, test.Equals, token) 63 c.Assert(response.Type, test.Equals, p.Response_SUCCESS_ATOM) 64 c.Assert(response.Responses, test.HasLen, 1) 65 c.Assert(response.Responses[0], test.DeepEquals, json.RawMessage([]byte(`"response"`))) 66 c.Assert(cursor, test.NotNil) 67 c.Assert(cursor.token, test.Equals, token) 68 c.Assert(cursor.conn, test.Equals, connection) 69 c.Assert(cursor.ctx, test.Equals, ctx) 70 c.Assert(cursor.responses, test.DeepEquals, response.Responses) 71 c.Assert(err, test.IsNil) 72 conn.AssertExpectations(c) 73 } 74 75 func (s *ConnectionSuite) TestConnection_Query_DefaultDBOk(c *test.C) { 76 ctx := context.Background() 77 token := int64(1) 78 q := testQuery(Table("table").Get("id")) 79 q2 := q 80 q2.Opts["db"], _ = DB("db").Build() 81 writeData := serializeQuery(token, q2) 82 respData := serializeAtomResponse() 83 header := respHeader(token, respData) 84 85 conn := &connMock{} 86 conn.On("Write", writeData).Return(len(writeData), nil, nil) 87 conn.On("Read", respHeaderLen).Return(header, respHeaderLen, nil, nil) 88 conn.On("Read", len(respData)).Return(respData, len(respData), nil, nil) 89 conn.On("Close").Return(nil) 90 91 connection := newConnection(conn, "addr", &ConnectOpts{Database: "db"}) 92 done := runConnection(connection) 93 response, cursor, err := connection.Query(ctx, q) 94 connection.Close() 95 <-done 96 97 c.Assert(response, test.NotNil) 98 c.Assert(response.Token, test.Equals, token) 99 c.Assert(response.Type, test.Equals, p.Response_SUCCESS_ATOM) 100 c.Assert(response.Responses, test.HasLen, 1) 101 c.Assert(response.Responses[0], test.DeepEquals, json.RawMessage([]byte(`"response"`))) 102 c.Assert(cursor, test.NotNil) 103 c.Assert(cursor.token, test.Equals, token) 104 c.Assert(cursor.conn, test.Equals, connection) 105 c.Assert(cursor.ctx, test.Equals, ctx) 106 c.Assert(cursor.responses, test.DeepEquals, response.Responses) 107 c.Assert(err, test.IsNil) 108 conn.AssertExpectations(c) 109 } 110 111 func (s *ConnectionSuite) TestConnection_Query_Nil(c *test.C) { 112 response, cursor, err := (*Connection)(nil).Query(nil, Query{}) 113 c.Assert(err, test.Equals, ErrConnectionClosed) 114 c.Assert(response, test.IsNil) 115 c.Assert(cursor, test.IsNil) 116 } 117 118 func (s *ConnectionSuite) TestConnection_Query_NilConn(c *test.C) { 119 connection := newConnection(nil, "addr", &ConnectOpts{Database: "db"}) 120 response, cursor, err := connection.Query(nil, Query{}) 121 c.Assert(err, test.Equals, ErrConnectionClosed) 122 c.Assert(response, test.IsNil) 123 c.Assert(cursor, test.IsNil) 124 } 125 126 func (s *ConnectionSuite) TestConnection_Query_SendFail(c *test.C) { 127 ctx := context.Background() 128 token := int64(1) 129 q := testQuery(DB("db").Table("table").Get("id")) 130 writeData := serializeQuery(token, q) 131 132 conn := &connMock{} 133 conn.On("Write", writeData).Return(0, io.EOF, nil) 134 135 connection := newConnection(conn, "addr", &ConnectOpts{}) 136 response, cursor, err := connection.Query(ctx, q) 137 138 c.Assert(response, test.IsNil) 139 c.Assert(cursor, test.IsNil) 140 c.Assert(err, test.Equals, RQLConnectionError{rqlError(io.EOF.Error())}) 141 conn.AssertExpectations(c) 142 } 143 144 func (s *ConnectionSuite) TestConnection_Query_NoReplyOk(c *test.C) { 145 token := int64(1) 146 q := testQuery(DB("db").Table("table").Get("id")) 147 q.Opts["noreply"] = true 148 writeData := serializeQuery(token, q) 149 respData := serializeAtomResponse() 150 header := respHeader(token, respData) 151 152 conn := &connMock{} 153 conn.On("Write", writeData).Return(len(writeData), nil, nil) 154 conn.On("Read", respHeaderLen).Return(header, respHeaderLen, nil, nil) 155 conn.On("Read", len(respData)).Return(respData, len(respData), nil, nil) 156 conn.On("Close").Return(nil) 157 158 connection := newConnection(conn, "addr", &ConnectOpts{}) 159 done := runConnection(connection) 160 response, cursor, err := connection.Query(nil, q) 161 connection.Close() 162 <-done 163 164 c.Assert(response, test.IsNil) 165 c.Assert(cursor, test.IsNil) 166 c.Assert(err, test.IsNil) 167 conn.AssertExpectations(c) 168 } 169 170 func (s *ConnectionSuite) TestConnection_Query_TimeoutWrite(c *test.C) { 171 ctx, cancel := context.WithCancel(context.Background()) 172 token := int64(1) 173 q := testQuery(DB("db").Table("table").Get("id")) 174 writeData := serializeQuery(token, q) 175 stopData := serializeQuery(token, newStopQuery(token)) 176 177 conn := &connMock{} 178 conn.On("Write", writeData).Return(len(writeData), nil, nil) 179 conn.On("Write", stopData).Return(len(stopData), nil, nil) 180 181 connection := newConnection(conn, "addr", &ConnectOpts{ReadTimeout: time.Millisecond, WriteTimeout: time.Millisecond}) 182 connection.readRequestsChan = make(chan tokenAndPromise, 0) 183 cancel() 184 response, cursor, err := connection.Query(ctx, q) 185 186 c.Assert(response, test.IsNil) 187 c.Assert(cursor, test.IsNil) 188 c.Assert(err, test.Equals, ErrQueryTimeout) 189 conn.AssertExpectations(c) 190 } 191 192 func (s *ConnectionSuite) TestConnection_Query_TimeoutRead(c *test.C) { 193 ctx, _ := context.WithTimeout(context.Background(), 5*time.Millisecond) 194 token := int64(1) 195 q := testQuery(DB("db").Table("table").Get("id")) 196 writeData := serializeQuery(token, q) 197 stopData := serializeQuery(token, newStopQuery(token)) 198 199 conn := &connMock{} 200 conn.On("Write", writeData).Return(len(writeData), nil, 10*time.Millisecond) 201 conn.On("Write", stopData).Return(len(stopData), nil, nil) 202 203 connection := newConnection(conn, "addr", &ConnectOpts{ReadTimeout: time.Millisecond, WriteTimeout: time.Millisecond}) 204 response, cursor, err := connection.Query(ctx, q) 205 206 c.Assert(response, test.IsNil) 207 c.Assert(cursor, test.IsNil) 208 c.Assert(err, test.Equals, ErrQueryTimeout) 209 conn.AssertExpectations(c) 210 } 211 212 func (s *ConnectionSuite) TestConnection_Query_SendFailTracing(c *test.C) { 213 tracer := mocktracer.New() 214 rootSpan := tracer.StartSpan("root") 215 ctx := opentracing.ContextWithSpan(context.Background(), rootSpan) 216 token := int64(1) 217 q := testQuery(DB("db").Table("table").Get("id")) 218 writeData := serializeQuery(token, q) 219 220 conn := &connMock{} 221 conn.On("Write", writeData).Return(0, io.EOF, nil) 222 223 connection := newConnection(conn, "addr", &ConnectOpts{UseOpentracing: true}) 224 response, cursor, err := connection.Query(ctx, q) 225 226 c.Assert(response, test.IsNil) 227 c.Assert(cursor, test.IsNil) 228 c.Assert(err, test.Equals, RQLConnectionError{rqlError(io.EOF.Error())}) 229 conn.AssertExpectations(c) 230 c.Assert(tracer.FinishedSpans(), test.HasLen, 2) 231 } 232 233 func (s *ConnectionSuite) TestConnection_processResponses_SocketErr(c *test.C) { 234 promise1 := make(chan responseAndCursor, 1) 235 promise2 := make(chan responseAndCursor, 1) 236 promise3 := make(chan responseAndCursor, 1) 237 238 conn := &connMock{} 239 connection := newConnection(conn, "addr", &ConnectOpts{}) 240 241 conn.On("Close").Return(nil).Run(func(args mock.Arguments) { 242 close(connection.responseChan) 243 }) 244 245 done := make(chan struct{}) 246 go func() { 247 connection.processResponses() 248 close(done) 249 }() 250 251 connection.readRequestsChan <- tokenAndPromise{query: &Query{Token: 1}, promise: promise1} 252 connection.readRequestsChan <- tokenAndPromise{query: &Query{Token: 2}, promise: promise2} 253 connection.readRequestsChan <- tokenAndPromise{query: &Query{Token: 2}, promise: promise3} 254 time.Sleep(5 * time.Millisecond) 255 connection.responseChan <- responseAndError{err: io.EOF} 256 <-done 257 258 select { 259 case f := <-promise1: 260 c.Assert(f.err, test.Equals, io.EOF) 261 c.Assert(f.response, test.IsNil) 262 default: 263 c.Fail() 264 } 265 select { 266 case f := <-promise2: 267 c.Assert(f.err, test.Equals, io.EOF) 268 c.Assert(f.response, test.IsNil) 269 default: 270 c.Fail() 271 } 272 select { 273 case f := <-promise3: 274 c.Assert(f.err, test.Equals, io.EOF) 275 c.Assert(f.response, test.IsNil) 276 default: 277 c.Fail() 278 } 279 conn.AssertExpectations(c) 280 } 281 282 func (s *ConnectionSuite) TestConnection_processResponses_StopOk(c *test.C) { 283 promise1 := make(chan responseAndCursor, 1) 284 285 connection := newConnection(nil, "addr", &ConnectOpts{}) 286 287 done := make(chan struct{}) 288 go func() { 289 connection.processResponses() 290 close(done) 291 }() 292 293 connection.readRequestsChan <- tokenAndPromise{query: &Query{Token: 1}, promise: promise1} 294 time.Sleep(5 * time.Millisecond) 295 close(connection.responseChan) 296 <-done 297 298 select { 299 case f := <-promise1: 300 c.Assert(f.err, test.Equals, ErrConnectionClosed) 301 c.Assert(f.response, test.IsNil) 302 default: 303 c.Fail() 304 } 305 } 306 307 func (s *ConnectionSuite) TestConnection_processResponses_ResponseFirst(c *test.C) { 308 promise1 := make(chan responseAndCursor, 1) 309 response1 := &Response{Token: 1, Type: p.Response_RUNTIME_ERROR, ErrorType: p.Response_INTERNAL} 310 311 conn := &connMock{} 312 conn.On("Close").Return(nil) 313 314 connection := newConnection(conn, "addr", &ConnectOpts{}) 315 316 go connection.processResponses() 317 318 connection.responseChan <- responseAndError{response: response1} 319 time.Sleep(5 * time.Millisecond) 320 connection.readRequestsChan <- tokenAndPromise{query: &Query{Token: 1}, promise: promise1} 321 time.Sleep(5 * time.Millisecond) 322 connection.Close() 323 time.Sleep(5 * time.Millisecond) 324 325 select { 326 case f := <-promise1: 327 c.Assert(f.err, test.FitsTypeOf, RQLInternalError{}) 328 c.Assert(f.response, test.Equals, response1) 329 c.Assert(f.cursor, test.IsNil) 330 default: 331 c.Fail() 332 } 333 conn.AssertExpectations(c) 334 } 335 336 func (s *ConnectionSuite) TestConnection_readResponse_TimeoutHeader(c *test.C) { 337 timeout := time.Second 338 339 conn := &connMock{} 340 conn.On("Read", respHeaderLen).Return(nil, 0, io.EOF, nil) 341 342 connection := newConnection(conn, "addr", &ConnectOpts{ReadTimeout: timeout}) 343 344 response, err := connection.readResponse() 345 346 c.Assert(response, test.IsNil) 347 c.Assert(err, test.FitsTypeOf, RQLConnectionError{}) 348 c.Assert(connection.isBad(), test.Equals, true) 349 conn.AssertExpectations(c) 350 } 351 352 func (s *ConnectionSuite) TestConnection_readResponse_BodySocketErr(c *test.C) { 353 token := int64(5) 354 respData := serializeAtomResponse() 355 header := respHeader(token, respData) 356 357 conn := &connMock{} 358 conn.On("Read", respHeaderLen).Return(header, len(header), nil, nil) 359 conn.On("Read", len(respData)).Return(nil, 0, io.EOF, nil) 360 361 connection := newConnection(conn, "addr", &ConnectOpts{}) 362 363 response, err := connection.readResponse() 364 365 c.Assert(response, test.IsNil) 366 c.Assert(err, test.FitsTypeOf, RQLConnectionError{}) 367 c.Assert(connection.isBad(), test.Equals, true) 368 conn.AssertExpectations(c) 369 } 370 371 func (s *ConnectionSuite) TestConnection_readResponse_BodyUnmarshalErr(c *test.C) { 372 token := int64(5) 373 respData := serializeAtomResponse() 374 header := respHeader(token, respData) 375 376 conn := &connMock{} 377 conn.On("Read", respHeaderLen).Return(header, len(header), nil, nil) 378 conn.On("Read", len(respData)).Return(make([]byte, len(respData)), len(respData), nil, nil) 379 380 connection := newConnection(conn, "addr", &ConnectOpts{}) 381 382 response, err := connection.readResponse() 383 384 c.Assert(response, test.IsNil) 385 c.Assert(err, test.FitsTypeOf, RQLDriverError{}) 386 c.Assert(connection.isBad(), test.Equals, true) 387 conn.AssertExpectations(c) 388 } 389 390 func (s *ConnectionSuite) TestConnection_processResponse_ClientErrOk(c *test.C) { 391 ctx := context.Background() 392 token := int64(3) 393 q := Query{Token: token} 394 response := &Response{Token: token, Type: p.Response_CLIENT_ERROR} 395 396 connection := newConnection(nil, "addr", &ConnectOpts{}) 397 398 resp, cursor, err := connection.processResponse(ctx, q, response, nil) 399 400 c.Assert(resp, test.Equals, response) 401 c.Assert(cursor, test.IsNil) 402 c.Assert(err, test.FitsTypeOf, RQLClientError{}) 403 } 404 405 func (s *ConnectionSuite) TestConnection_processResponse_CompileErrOk(c *test.C) { 406 ctx := context.Background() 407 token := int64(3) 408 q := Query{Token: token} 409 response := &Response{Token: token, Type: p.Response_COMPILE_ERROR} 410 411 connection := newConnection(nil, "addr", &ConnectOpts{}) 412 413 resp, cursor, err := connection.processResponse(ctx, q, response, nil) 414 415 c.Assert(resp, test.Equals, response) 416 c.Assert(cursor, test.IsNil) 417 c.Assert(err, test.FitsTypeOf, RQLCompileError{}) 418 } 419 420 func (s *ConnectionSuite) TestConnection_processResponse_RuntimeErrOk(c *test.C) { 421 tracer := mocktracer.New() 422 rootSpan := tracer.StartSpan("root") 423 ctx := opentracing.ContextWithSpan(context.Background(), rootSpan) 424 qSpan := rootSpan.Tracer().StartSpan("q", opentracing.ChildOf(rootSpan.Context())) 425 426 token := int64(3) 427 term := Table("test") 428 q := Query{Token: token, Term: &term} 429 response := &Response{Token: token, Type: p.Response_RUNTIME_ERROR, Responses: []json.RawMessage{{'e', 'r', 'r'}}} 430 431 connection := newConnection(nil, "addr", &ConnectOpts{}) 432 433 resp, cursor, err := connection.processResponse(ctx, q, response, qSpan) 434 435 c.Assert(resp, test.Equals, response) 436 c.Assert(cursor, test.IsNil) 437 c.Assert(err, test.FitsTypeOf, RQLRuntimeError{}) 438 c.Assert(tracer.FinishedSpans(), test.HasLen, 1) 439 c.Assert(tracer.FinishedSpans()[0].Tags()["error"], test.Equals, true) 440 } 441 442 func (s *ConnectionSuite) TestConnection_processResponse_FirstPartialOk(c *test.C) { 443 ctx := context.Background() 444 token := int64(3) 445 q := Query{Token: token} 446 rawResponse1 := json.RawMessage{1, 2, 3} 447 rawResponse2 := json.RawMessage{3, 4, 5} 448 response := &Response{Token: token, Type: p.Response_SUCCESS_PARTIAL, Responses: []json.RawMessage{rawResponse1, rawResponse2}} 449 450 connection := newConnection(nil, "addr", &ConnectOpts{}) 451 452 resp, cursor, err := connection.processResponse(ctx, q, response, nil) 453 454 c.Assert(resp, test.Equals, response) 455 c.Assert(cursor, test.NotNil) 456 c.Assert(cursor.token, test.Equals, token) 457 c.Assert(cursor.ctx, test.Equals, ctx) 458 c.Assert(cursor.responses, test.HasLen, 2) 459 c.Assert(cursor.responses[0], test.DeepEquals, rawResponse1) 460 c.Assert(cursor.responses[1], test.DeepEquals, rawResponse2) 461 c.Assert(cursor.conn, test.Equals, connection) 462 c.Assert(err, test.IsNil) 463 c.Assert(connection.cursors, test.HasLen, 1) 464 c.Assert(connection.cursors[token], test.Equals, cursor) 465 } 466 467 func (s *ConnectionSuite) TestConnection_processResponse_PartialOk(c *test.C) { 468 ctx := context.Background() 469 token := int64(3) 470 term := Table("test") 471 q := Query{Token: token} 472 rawResponse1 := json.RawMessage{1, 2, 3} 473 rawResponse2 := json.RawMessage{3, 4, 5} 474 response := &Response{Token: token, Type: p.Response_SUCCESS_PARTIAL, Responses: []json.RawMessage{rawResponse1, rawResponse2}} 475 476 connection := newConnection(nil, "addr", &ConnectOpts{}) 477 oldCursor := newCursor(ctx, connection, "Cursor", token, &term, q.Opts) 478 connection.cursors[token] = oldCursor 479 480 resp, cursor, err := connection.processResponse(ctx, q, response, nil) 481 482 c.Assert(resp, test.Equals, response) 483 c.Assert(cursor, test.Equals, oldCursor) 484 c.Assert(cursor.responses, test.HasLen, 2) 485 c.Assert(cursor.responses[0], test.DeepEquals, rawResponse1) 486 c.Assert(cursor.responses[1], test.DeepEquals, rawResponse2) 487 c.Assert(err, test.IsNil) 488 c.Assert(connection.cursors, test.HasLen, 1) 489 c.Assert(connection.cursors[token], test.Equals, cursor) 490 } 491 492 func (s *ConnectionSuite) TestConnection_processResponse_SequenceOk(c *test.C) { 493 tracer := mocktracer.New() 494 rootSpan := tracer.StartSpan("root") 495 ctx := opentracing.ContextWithSpan(context.Background(), rootSpan) 496 qSpan := rootSpan.Tracer().StartSpan("q", opentracing.ChildOf(rootSpan.Context())) 497 498 token := int64(3) 499 q := Query{Token: token} 500 rawResponse1 := json.RawMessage{1, 2, 3} 501 rawResponse2 := json.RawMessage{3, 4, 5} 502 response := &Response{Token: token, Type: p.Response_SUCCESS_SEQUENCE, Responses: []json.RawMessage{rawResponse1, rawResponse2}} 503 504 connection := newConnection(nil, "addr", &ConnectOpts{}) 505 506 resp, cursor, err := connection.processResponse(ctx, q, response, qSpan) 507 508 c.Assert(resp, test.Equals, response) 509 c.Assert(cursor, test.NotNil) 510 c.Assert(cursor.token, test.Equals, token) 511 c.Assert(cursor.ctx, test.Equals, ctx) 512 c.Assert(cursor.responses, test.HasLen, 2) 513 c.Assert(cursor.responses[0], test.DeepEquals, rawResponse1) 514 c.Assert(cursor.responses[1], test.DeepEquals, rawResponse2) 515 c.Assert(cursor.conn, test.Equals, connection) 516 c.Assert(err, test.IsNil) 517 c.Assert(connection.cursors, test.HasLen, 0) 518 c.Assert(tracer.FinishedSpans(), test.HasLen, 1) 519 c.Assert(tracer.FinishedSpans()[0].Tags(), test.HasLen, 0) 520 } 521 522 func (s *ConnectionSuite) TestConnection_processResponse_WaitOk(c *test.C) { 523 ctx := context.Background() 524 token := int64(3) 525 q := Query{Token: token} 526 response := &Response{Token: token, Type: p.Response_WAIT_COMPLETE} 527 528 connection := newConnection(nil, "addr", &ConnectOpts{}) 529 connection.cursors[token] = &Cursor{} 530 531 resp, cursor, err := connection.processResponse(ctx, q, response, nil) 532 533 c.Assert(resp, test.Equals, response) 534 c.Assert(cursor, test.IsNil) 535 c.Assert(err, test.IsNil) 536 c.Assert(connection.cursors, test.HasLen, 0) 537 } 538 539 func (s *ConnectionSuite) TestConnection_processResponse_UnexpectedOk(c *test.C) { 540 ctx := context.Background() 541 token := int64(3) 542 q := Query{Token: token} 543 response := &Response{Token: token, Type: 99} 544 545 connection := newConnection(nil, "addr", &ConnectOpts{}) 546 547 resp, cursor, err := connection.processResponse(ctx, q, response, nil) 548 549 c.Assert(resp, test.IsNil) 550 c.Assert(cursor, test.IsNil) 551 c.Assert(err, test.FitsTypeOf, RQLDriverError{}) 552 } 553 554 func testQuery(t Term) Query { 555 q, _ := newQuery( 556 t, 557 map[string]interface{}{}, 558 &ConnectOpts{}, 559 ) 560 return q 561 } 562 563 func respHeader(token int64, msg []byte) []byte { 564 header := [respHeaderLen]byte{} 565 binary.LittleEndian.PutUint64(header[:], uint64(token)) 566 binary.LittleEndian.PutUint32(header[8:], uint32(len(msg))) 567 return header[:] 568 } 569 570 func serializeQuery(token int64, q Query) []byte { 571 b, _ := json.Marshal(q.Build()) 572 msg := make([]byte, len(b)+respHeaderLen+1) 573 binary.LittleEndian.PutUint64(msg, uint64(token)) 574 binary.LittleEndian.PutUint32(msg[8:], uint32(len(b)+1)) 575 copy(msg[respHeaderLen:], b) 576 msg[len(msg)-1] = '\n' // encoder.Marshal do this, json.Marshal doesn't 577 return msg 578 } 579 580 func serializeAtomResponse() []byte { 581 b, _ := json.Marshal(map[string]interface{}{ 582 "t": p.Response_SUCCESS_ATOM, 583 "r": []interface{}{"response"}, 584 }) 585 return b 586 }