comparison UnitTests/ServerIndex.cpp @ 209:9960642f0f45

abstraction of RestApi
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 28 Nov 2012 17:22:07 +0100
parents de640de989b8
children 0200cd330582
comparison
equal deleted inserted replaced
208:de640de989b8 209:9960642f0f45
255 255
256 listener.Reset(); 256 listener.Reset();
257 index.DeleteResource(a[6]); 257 index.DeleteResource(a[6]);
258 ASSERT_EQ("", listener.ancestorId_); // No more ancestor 258 ASSERT_EQ("", listener.ancestorId_); // No more ancestor
259 } 259 }
260
261
262
263
264 #include "../Core/HttpServer/FilesystemHttpSender.h"
265
266 #include "../Core/Toolbox.h"
267 #include "../Core/HttpServer/HttpOutput.h"
268 #include "../Core/HttpServer/HttpHandler.h"
269
270 #include "../Core/HttpServer/HttpFileSender.h"
271
272
273 namespace Orthanc
274 {
275 class RestApiPath
276 {
277 private:
278 UriComponents uri_;
279 bool hasTrailing_;
280 std::vector<std::string> components_;
281
282 public:
283 typedef std::map<std::string, std::string> Components;
284
285 RestApiPath(const std::string& uri)
286 {
287 Toolbox::SplitUriComponents(uri_, uri);
288
289 if (uri_.size() == 0)
290 {
291 return;
292 }
293
294 if (uri_.back() == "*")
295 {
296 hasTrailing_ = true;
297 uri_.pop_back();
298 }
299 else
300 {
301 hasTrailing_ = false;
302 }
303
304 components_.resize(uri_.size());
305 for (size_t i = 0; i < uri_.size(); i++)
306 {
307 size_t s = uri_[i].size();
308 assert(s > 0);
309
310 if (uri_[i][0] == '{' &&
311 uri_[i][s - 1] == '}')
312 {
313 components_[i] = uri_[i].substr(1, s - 2);
314 uri_[i] = "";
315 }
316 else
317 {
318 components_[i] = "";
319 }
320 }
321 }
322
323 // This version is slower
324 bool Match(Components& components,
325 UriComponents& trailing,
326 const std::string& uriRaw) const
327 {
328 UriComponents uri;
329 Toolbox::SplitUriComponents(uri, uriRaw);
330 return Match(components, trailing, uri);
331 }
332
333 bool Match(Components& components,
334 UriComponents& trailing,
335 const UriComponents& uri) const
336 {
337 if (uri.size() < uri_.size())
338 {
339 return false;
340 }
341
342 if (!hasTrailing_ && uri.size() > uri_.size())
343 {
344 return false;
345 }
346
347 components.clear();
348 trailing.clear();
349
350 assert(uri_.size() <= uri.size());
351 for (size_t i = 0; i < uri_.size(); i++)
352 {
353 if (components_[i].size() == 0)
354 {
355 // This URI component is not a free parameter
356 if (uri_[i] != uri[i])
357 {
358 return false;
359 }
360 }
361 else
362 {
363 // This URI component is a free parameter
364 components[components_[i]] = uri[i];
365 }
366 }
367
368 if (hasTrailing_)
369 {
370 trailing.assign(uri.begin() + uri_.size(), uri.end());
371 }
372
373 return true;
374 }
375
376 bool Match(const UriComponents& uri) const
377 {
378 Components components;
379 UriComponents trailing;
380 return Match(components, trailing, uri);
381 }
382 };
383
384
385 class RestApiOutput
386 {
387 private:
388 HttpOutput& output_;
389
390 public:
391 RestApiOutput(HttpOutput& output) : output_(output)
392 {
393 }
394
395 void AnswerFile(HttpFileSender& sender)
396 {
397 sender.Send(output_);
398 }
399
400 void AnswerJson(const Json::Value& value)
401 {
402 Json::StyledWriter writer;
403 std::string s = writer.write(value);
404 output_.AnswerBufferWithContentType(s, "application/json");
405 }
406
407 void AnswerBuffer(const std::string& buffer,
408 const std::string& contentType)
409 {
410 output_.AnswerBufferWithContentType(buffer, contentType);
411 }
412
413 void Redirect(const char* path)
414 {
415 output_.Redirect(path);
416 }
417 };
418
419
420 class RestApiSharedCall
421 {
422 protected:
423 RestApiOutput* output_;
424 IDynamicObject* context_;
425 const HttpHandler::Arguments* httpHeaders_;
426 const RestApiPath::Components* uriComponents_;
427 const UriComponents* trailing_;
428
429 public:
430 RestApiOutput& GetOutput()
431 {
432 return *output_;
433 }
434
435 IDynamicObject* GetContext()
436 {
437 return context_;
438 }
439
440 const HttpHandler::Arguments& GetHttpHeaders() const
441 {
442 return *httpHeaders_;
443 }
444
445 const RestApiPath::Components& GetUriComponents() const
446 {
447 return *uriComponents_;
448 }
449
450 const UriComponents& GetTrailing() const
451 {
452 return *trailing_;
453 }
454
455 std::string GetUriComponent(const std::string& name,
456 const std::string& defaultValue)
457 {
458 return HttpHandler::GetArgument(*uriComponents_, name, defaultValue);
459 }
460 };
461
462
463 class RestApiPutCall : public RestApiSharedCall
464 {
465 friend class RestApi;
466
467 private:
468 const std::string* data_;
469
470 public:
471 const std::string& GetData()
472 {
473 return *data_;
474 }
475 };
476
477
478 class RestApiPostCall : public RestApiSharedCall
479 {
480 friend class RestApi;
481
482 private:
483 const std::string* data_;
484
485 public:
486 const std::string& GetData()
487 {
488 return *data_;
489 }
490 };
491
492
493
494 class RestApiDeleteCall : public RestApiSharedCall
495 {
496 friend class RestApi;
497 };
498
499
500
501
502 class RestApiGetCall : public RestApiSharedCall
503 {
504 friend class RestApi;
505
506 private:
507 const HttpHandler::Arguments* getArguments_;
508
509 public:
510 std::string GetArgument(const std::string& name,
511 const std::string& defaultValue)
512 {
513 return HttpHandler::GetArgument(*getArguments_, name, defaultValue);
514 }
515 };
516
517
518
519 class RestApi : public HttpHandler
520 {
521 public:
522 typedef void (*GetHandler) (RestApiGetCall& call);
523
524 typedef void (*DeleteHandler) (RestApiDeleteCall& call);
525
526 typedef void (*PutHandler) (RestApiPutCall& call);
527
528 typedef void (*PostHandler) (RestApiPostCall& call);
529
530 private:
531 typedef std::list< std::pair<RestApiPath*, GetHandler> > GetHandlers;
532 typedef std::list< std::pair<RestApiPath*, PutHandler> > PutHandlers;
533 typedef std::list< std::pair<RestApiPath*, PostHandler> > PostHandlers;
534 typedef std::list< std::pair<RestApiPath*, DeleteHandler> > DeleteHandlers;
535
536 // TODO MUTEX BETWEEN CONTEXTS !!!
537 std::auto_ptr<IDynamicObject> context_;
538
539 GetHandlers getHandlers_;
540 PutHandlers putHandlers_;
541 PostHandlers postHandlers_;
542 DeleteHandlers deleteHandlers_;
543
544 bool IsGetAccepted(const UriComponents& uri)
545 {
546 for (GetHandlers::const_iterator it = getHandlers_.begin();
547 it != getHandlers_.end(); it++)
548 {
549 if (it->first->Match(uri))
550 {
551 return true;
552 }
553 }
554
555 return false;
556 }
557
558 bool IsPutAccepted(const UriComponents& uri)
559 {
560 for (PutHandlers::const_iterator it = putHandlers_.begin();
561 it != putHandlers_.end(); it++)
562 {
563 if (it->first->Match(uri))
564 {
565 return true;
566 }
567 }
568
569 return false;
570 }
571
572 bool IsPostAccepted(const UriComponents& uri)
573 {
574 for (PostHandlers::const_iterator it = postHandlers_.begin();
575 it != postHandlers_.end(); it++)
576 {
577 if (it->first->Match(uri))
578 {
579 return true;
580 }
581 }
582
583 return false;
584 }
585
586 bool IsDeleteAccepted(const UriComponents& uri)
587 {
588 for (DeleteHandlers::const_iterator it = deleteHandlers_.begin();
589 it != deleteHandlers_.end(); it++)
590 {
591 if (it->first->Match(uri))
592 {
593 return true;
594 }
595 }
596
597 return false;
598 }
599
600 void AddMethod(std::string& target,
601 const std::string& method) const
602 {
603 if (target.size() > 0)
604 target += "," + method;
605 else
606 target = method;
607 }
608
609 std::string GetAcceptedMethods(const UriComponents& uri)
610 {
611 std::string s;
612
613 if (IsGetAccepted(uri))
614 AddMethod(s, "GET");
615
616 if (IsPutAccepted(uri))
617 AddMethod(s, "PUT");
618
619 if (IsPostAccepted(uri))
620 AddMethod(s, "POST");
621
622 if (IsDeleteAccepted(uri))
623 AddMethod(s, "DELETE");
624
625 return s;
626 }
627
628 public:
629 RestApi()
630 {
631 }
632
633 void SetContext(IDynamicObject* context) // This takes the ownership
634 {
635 context_.reset(context);
636 }
637
638 ~RestApi()
639 {
640 for (GetHandlers::iterator it = getHandlers_.begin();
641 it != getHandlers_.end(); it++)
642 {
643 delete it->first;
644 }
645
646 for (PutHandlers::iterator it = putHandlers_.begin();
647 it != putHandlers_.end(); it++)
648 {
649 delete it->first;
650 }
651
652 for (PostHandlers::iterator it = postHandlers_.begin();
653 it != postHandlers_.end(); it++)
654 {
655 delete it->first;
656 }
657
658 for (DeleteHandlers::iterator it = deleteHandlers_.begin();
659 it != deleteHandlers_.end(); it++)
660 {
661 delete it->first;
662 }
663 }
664
665 virtual bool IsServedUri(const UriComponents& uri)
666 {
667 return (IsGetAccepted(uri) ||
668 IsPutAccepted(uri) ||
669 IsPostAccepted(uri) ||
670 IsDeleteAccepted(uri));
671 }
672
673 virtual void Handle(HttpOutput& output,
674 const std::string& method,
675 const UriComponents& uri,
676 const Arguments& headers,
677 const Arguments& getArguments,
678 const std::string& postData)
679 {
680 bool ok = false;
681 RestApiOutput restOutput(output);
682 RestApiPath::Components components;
683 UriComponents trailing;
684
685 if (method == "GET")
686 {
687 for (GetHandlers::const_iterator it = getHandlers_.begin();
688 it != getHandlers_.end(); it++)
689 {
690 if (it->first->Match(components, trailing, uri))
691 {
692 ok = true;
693 RestApiGetCall call;
694 call.output_ = &restOutput;
695 call.context_ = context_.get();
696 call.httpHeaders_ = &headers;
697 call.uriComponents_ = &components;
698 call.trailing_ = &trailing;
699
700 call.getArguments_ = &getArguments;
701 it->second(call);
702 }
703 }
704 }
705 else if (method == "PUT")
706 {
707 for (PutHandlers::const_iterator it = putHandlers_.begin();
708 it != putHandlers_.end(); it++)
709 {
710 if (it->first->Match(components, trailing, uri))
711 {
712 ok = true;
713 RestApiPutCall call;
714 call.output_ = &restOutput;
715 call.context_ = context_.get();
716 call.httpHeaders_ = &headers;
717 call.uriComponents_ = &components;
718 call.trailing_ = &trailing;
719
720 call.data_ = &postData;
721 it->second(call);
722 }
723 }
724 }
725 else if (method == "POST")
726 {
727 for (PostHandlers::const_iterator it = postHandlers_.begin();
728 it != postHandlers_.end(); it++)
729 {
730 if (it->first->Match(components, trailing, uri))
731 {
732 ok = true;
733 RestApiPostCall call;
734 call.output_ = &restOutput;
735 call.context_ = context_.get();
736 call.httpHeaders_ = &headers;
737 call.uriComponents_ = &components;
738 call.trailing_ = &trailing;
739
740 call.data_ = &postData;
741 it->second(call);
742 }
743 }
744 }
745 else if (method == "DELETE")
746 {
747 for (DeleteHandlers::const_iterator it = deleteHandlers_.begin();
748 it != deleteHandlers_.end(); it++)
749 {
750 if (it->first->Match(components, trailing, uri))
751 {
752 ok = true;
753 RestApiDeleteCall call;
754 call.output_ = &restOutput;
755 call.context_ = context_.get();
756 call.httpHeaders_ = &headers;
757 call.uriComponents_ = &components;
758 call.trailing_ = &trailing;
759 it->second(call);
760 }
761 }
762 }
763
764 if (!ok)
765 {
766 output.SendMethodNotAllowedError(GetAcceptedMethods(uri));
767 }
768 }
769
770 void Register(const std::string& path,
771 GetHandler handler)
772 {
773 getHandlers_.push_back(std::make_pair(new RestApiPath(path), handler));
774 }
775
776
777 void Register(const std::string& path,
778 PutHandler handler)
779 {
780 putHandlers_.push_back(std::make_pair(new RestApiPath(path), handler));
781 }
782
783
784 void Register(const std::string& path,
785 PostHandler handler)
786 {
787 postHandlers_.push_back(std::make_pair(new RestApiPath(path), handler));
788 }
789
790
791 void Register(const std::string& path,
792 DeleteHandler handler)
793 {
794 deleteHandlers_.push_back(std::make_pair(new RestApiPath(path), handler));
795 }
796
797 };
798
799 }
800
801
802 TEST(RestApi, RestApiPath)
803 {
804 RestApiPath::Components args;
805 UriComponents trail;
806
807 {
808 RestApiPath uri("/coucou/{abc}/d/*");
809 ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d/e/f/g"));
810 ASSERT_EQ(1u, args.size());
811 ASSERT_EQ(3u, trail.size());
812 ASSERT_EQ("moi", args["abc"]);
813 ASSERT_EQ("e", trail[0]);
814 ASSERT_EQ("f", trail[1]);
815 ASSERT_EQ("g", trail[2]);
816
817 ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi/f"));
818 ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d/"));
819 ASSERT_FALSE(uri.Match(args, trail, "/a/moi/d"));
820 ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi"));
821 }
822
823 {
824 RestApiPath uri("/coucou/{abc}/d");
825 ASSERT_FALSE(uri.Match(args, trail, "/coucou/moi/d/e/f/g"));
826 ASSERT_TRUE(uri.Match(args, trail, "/coucou/moi/d"));
827 ASSERT_EQ(1u, args.size());
828 ASSERT_EQ(0u, trail.size());
829 ASSERT_EQ("moi", args["abc"]);
830 }
831
832 {
833 RestApiPath uri("/*");
834 ASSERT_TRUE(uri.Match(args, trail, "/a/b/c"));
835 ASSERT_EQ(0u, args.size());
836 ASSERT_EQ(3u, trail.size());
837 ASSERT_EQ("a", trail[0]);
838 ASSERT_EQ("b", trail[1]);
839 ASSERT_EQ("c", trail[2]);
840 }
841 }
842
843
844
845
846 #include "../Core/HttpServer/MongooseServer.h"
847
848 struct Tutu : public IDynamicObject
849 {
850 static void Toto(RestApiGetCall& call)
851 {
852 printf("DONE\n");
853 Json::Value a = Json::objectValue;
854 a["Tutu"] = "Toto";
855 a["Youpie"] = call.GetArgument("coucou", "nope");
856 a["Toto"] = call.GetUriComponent("test", "nope");
857 call.GetOutput().AnswerJson(a);
858 }
859 };
860
861
862
863 TEST(RestApi, Tutu)
864 {
865 MongooseServer httpServer;
866 httpServer.SetPortNumber(8042);
867 httpServer.Start();
868
869 RestApi* api = new RestApi;
870 httpServer.RegisterHandler(api);
871 api->Register("/coucou/{test}/a/*", Tutu::Toto);
872
873 httpServer.Start();
874 /*LOG(WARNING) << "REST has started";
875 Toolbox::ServerBarrier();*/
876 }