comparison vamp-server/simple-server.cpp @ 158:0876b5e67afe

Improve error handling and extend tests for it
author Chris Cannam <cannam@all-day-breakfast.com>
date Fri, 20 Jan 2017 22:24:44 +0000
parents 80d85794d8cd
children 3eb00e5c76c4
comparison
equal deleted inserted replaced
157:5699fca64251 158:0876b5e67afe
243 } 243 }
244 return j; 244 return j;
245 } 245 }
246 246
247 RequestOrResponse 247 RequestOrResponse
248 readRequestJson(string &err) 248 readRequestJson(string &err, bool &eof)
249 { 249 {
250 RequestOrResponse rr; 250 RequestOrResponse rr;
251 rr.direction = RequestOrResponse::Request; 251 rr.direction = RequestOrResponse::Request;
252 252
253 string input; 253 string input;
254 if (!getline(cin, input)) { 254 if (!getline(cin, input)) {
255 // the EOF case, not actually an error 255 // the EOF case, not actually an error
256 rr.type = RRType::NotValid; 256 eof = true;
257 return rr; 257 return rr;
258 } 258 }
259 259
260 Json j = convertRequestJson(input, err); 260 Json j = convertRequestJson(input, err);
261 if (err != "") return {}; 261 if (err != "") return {};
337 337
338 cout << j.dump() << endl; 338 cout << j.dump() << endl;
339 } 339 }
340 340
341 void 341 void
342 writeExceptionJson(const exception &e, RRType type) 342 writeExceptionJson(const exception &e, RRType type, RequestOrResponse::RpcId id)
343 { 343 {
344 Json j = VampJson::fromError(e.what(), type, Json()); 344 Json jid = writeJsonId(id);
345 Json j = VampJson::fromError(e.what(), type, jid);
345 cout << j.dump() << endl; 346 cout << j.dump() << endl;
346 } 347 }
347 348
348 RequestOrResponse 349 RequestOrResponse
349 readRequestCapnp() 350 readRequestCapnp(bool &eof)
350 { 351 {
351 RequestOrResponse rr; 352 RequestOrResponse rr;
352 rr.direction = RequestOrResponse::Request; 353 rr.direction = RequestOrResponse::Request;
353 354
354 static kj::FdInputStream stream(0); // stdin 355 static kj::FdInputStream stream(0); // stdin
355 static kj::BufferedInputStreamWrapper buffered(stream); 356 static kj::BufferedInputStreamWrapper buffered(stream);
356 357
357 if (buffered.tryGetReadBuffer() == nullptr) { 358 if (buffered.tryGetReadBuffer() == nullptr) {
358 rr.type = RRType::NotValid; 359 eof = true;
359 return rr; 360 return rr;
360 } 361 }
361 362
362 capnp::InputStreamMessageReader message(buffered); 363 capnp::InputStreamMessageReader message(buffered);
363 piper::RpcRequest::Reader reader = message.getRoot<piper::RpcRequest>(); 364 piper::RpcRequest::Reader reader = message.getRoot<piper::RpcRequest>();
428 429
429 writeMessageToFd(1, message); 430 writeMessageToFd(1, message);
430 } 431 }
431 432
432 void 433 void
433 writeExceptionCapnp(const exception &e, RRType type) 434 writeExceptionCapnp(const exception &e, RRType type, RequestOrResponse::RpcId id)
434 { 435 {
435 capnp::MallocMessageBuilder message; 436 capnp::MallocMessageBuilder message;
436 piper::RpcResponse::Builder builder = message.initRoot<piper::RpcResponse>(); 437 piper::RpcResponse::Builder builder = message.initRoot<piper::RpcResponse>();
438
439 buildId(builder, id);
437 VampnProto::buildRpcResponse_Exception(builder, e, type); 440 VampnProto::buildRpcResponse_Exception(builder, e, type);
438 441
439 writeMessageToFd(1, message); 442 writeMessageToFd(1, message);
440 } 443 }
441 444
470 break; 473 break;
471 474
472 case RRType::Configure: 475 case RRType::Configure:
473 { 476 {
474 auto &creq = request.configurationRequest; 477 auto &creq = request.configurationRequest;
478 if (!creq.plugin) {
479 throw runtime_error("unknown plugin handle supplied to configure");
480 }
481
475 auto h = mapper.pluginToHandle(creq.plugin); 482 auto h = mapper.pluginToHandle(creq.plugin);
476 if (mapper.isConfigured(h)) { 483 if (mapper.isConfigured(h)) {
477 throw runtime_error("plugin has already been configured"); 484 throw runtime_error("plugin has already been configured");
478 } 485 }
479 486
488 } 495 }
489 496
490 case RRType::Process: 497 case RRType::Process:
491 { 498 {
492 auto &preq = request.processRequest; 499 auto &preq = request.processRequest;
500 if (!preq.plugin) {
501 throw runtime_error("unknown plugin handle supplied to process");
502 }
503
493 auto h = mapper.pluginToHandle(preq.plugin); 504 auto h = mapper.pluginToHandle(preq.plugin);
494 if (!mapper.isConfigured(h)) { 505 if (!mapper.isConfigured(h)) {
495 throw runtime_error("plugin has not been configured"); 506 throw runtime_error("plugin has not been configured");
496 } 507 }
497 508
519 } 530 }
520 531
521 case RRType::Finish: 532 case RRType::Finish:
522 { 533 {
523 auto &freq = request.finishRequest; 534 auto &freq = request.finishRequest;
535 if (!freq.plugin) {
536 throw runtime_error("unknown plugin handle supplied to finish");
537 }
538
524 response.finishResponse.plugin = freq.plugin; 539 response.finishResponse.plugin = freq.plugin;
525 540
526 auto h = mapper.pluginToHandle(freq.plugin); 541 auto h = mapper.pluginToHandle(freq.plugin);
527 // Finish can be called (to unload the plugin) even if the 542 // Finish can be called (to unload the plugin) even if the
528 // plugin has never been configured or used. But we want to 543 // plugin has never been configured or used. But we want to
545 560
546 return response; 561 return response;
547 } 562 }
548 563
549 RequestOrResponse 564 RequestOrResponse
550 readRequest(string format) 565 readRequest(string format, bool &eof)
551 { 566 {
552 if (format == "capnp") { 567 if (format == "capnp") {
553 return readRequestCapnp(); 568 return readRequestCapnp(eof);
554 } else if (format == "json") { 569 } else if (format == "json") {
555 string err; 570 string err;
556 auto result = readRequestJson(err); 571 auto result = readRequestJson(err, eof);
557 if (err != "") throw runtime_error(err); 572 if (err != "") throw runtime_error(err);
558 else return result; 573 else return result;
559 } else { 574 } else {
560 throw runtime_error("unknown input format \"" + format + "\""); 575 throw runtime_error("unknown input format \"" + format + "\"");
561 } 576 }
574 } 589 }
575 suspendOutput(); 590 suspendOutput();
576 } 591 }
577 592
578 void 593 void
579 writeException(string format, const exception &e, RRType type) 594 writeException(string format, const exception &e, RRType type, RequestOrResponse::RpcId id)
580 { 595 {
581 resumeOutput(); 596 resumeOutput();
582 if (format == "capnp") { 597 if (format == "capnp") {
583 writeExceptionCapnp(e, type); 598 writeExceptionCapnp(e, type, id);
584 } else if (format == "json") { 599 } else if (format == "json") {
585 writeExceptionJson(e, type); 600 writeExceptionJson(e, type, id);
586 } else { 601 } else {
587 throw runtime_error("unknown output format \"" + format + "\""); 602 throw runtime_error("unknown output format \"" + format + "\"");
588 } 603 }
589 suspendOutput(); 604 suspendOutput();
590 } 605 }
642 657
643 RequestOrResponse request; 658 RequestOrResponse request;
644 659
645 try { 660 try {
646 661
647 request = readRequest(format); 662 bool eof = false;
663 request = readRequest(format, eof);
648 664
649 // NotValid without an exception indicates EOF: 665 if (eof) {
650 if (request.type == RRType::NotValid) {
651 if (debug) { 666 if (debug) {
652 cerr << myname << " " << pid << ": eof reached, exiting" << endl; 667 cerr << myname << " " << pid << ": eof reached, exiting" << endl;
653 } 668 }
654 break; 669 break;
655 } 670 }
657 if (debug) { 672 if (debug) {
658 cerr << myname << " " << pid << ": request received, of type " 673 cerr << myname << " " << pid << ": request received, of type "
659 << int(request.type) 674 << int(request.type)
660 << endl; 675 << endl;
661 } 676 }
662
663 RequestOrResponse response = handleRequest(request, debug);
664 response.id = request.id;
665
666 if (debug) {
667 cerr << myname << " " << pid << ": request handled, writing response"
668 << endl;
669 }
670
671 writeResponse(format, response);
672
673 if (debug) {
674 cerr << myname << " " << pid << ": response written" << endl;
675 }
676
677 if (request.type == RRType::Finish) {
678 auto h = mapper.pluginToHandle(request.finishRequest.plugin);
679 if (debug) {
680 cerr << myname << " " << pid << ": deleting the plugin with handle " << h << endl;
681 }
682 mapper.removePlugin(h);
683 delete request.finishRequest.plugin;
684 }
685 677
686 } catch (exception &e) { 678 } catch (exception &e) {
687 679
688 if (debug) { 680 if (debug) {
689 cerr << myname << " " << pid << ": error: " << e.what() << endl; 681 cerr << myname << " " << pid << ": error: " << e.what() << endl;
690 } 682 }
691 683
692 writeException(format, e, request.type); 684 writeException(format, e, request.type, request.id);
693 685
694 if (format == "capnp") { 686 if (format == "capnp") {
695 // Don't try to continue; we can't recover from a 687 // Don't try to continue; we can't recover from a
696 // mangled input stream. However, we can return a 688 // mangled input stream. However, we can return a
697 // successful error code because we are reporting the 689 // successful error code because we are reporting the
700 cerr << myname << " " << pid << ": not attempting to recover from capnp parse problems, exiting" << endl; 692 cerr << myname << " " << pid << ": not attempting to recover from capnp parse problems, exiting" << endl;
701 } 693 }
702 exit(0); 694 exit(0);
703 } 695 }
704 } 696 }
697
698 try {
699 RequestOrResponse response = handleRequest(request, debug);
700 response.id = request.id;
701
702 if (debug) {
703 cerr << myname << " " << pid << ": request handled, writing response"
704 << endl;
705 }
706
707 writeResponse(format, response);
708
709 if (debug) {
710 cerr << myname << " " << pid << ": response written" << endl;
711 }
712
713 if (request.type == RRType::Finish) {
714 auto h = mapper.pluginToHandle(request.finishRequest.plugin);
715 if (debug) {
716 cerr << myname << " " << pid << ": deleting the plugin with handle " << h << endl;
717 }
718 mapper.removePlugin(h);
719 delete request.finishRequest.plugin;
720 }
721
722 } catch (exception &e) {
723
724 if (debug) {
725 cerr << myname << " " << pid << ": error: " << e.what() << endl;
726 }
727
728 writeException(format, e, request.type, request.id);
729 }
705 } 730 }
706 731
707 exit(0); 732 exit(0);
708 } 733 }