Mercurial > hg > piper-cpp
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 } |