comparison UnitTestsSources/RestApiTests.cpp @ 1783:dbb07eb1a2f3

reorganization
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 18 Nov 2015 09:15:30 +0100
parents 9f2df9bb2cdf
children b1291df2f780
comparison
equal deleted inserted replaced
1782:9f2df9bb2cdf 1783:dbb07eb1a2f3
43 #include "../Core/RestApi/RestApi.h" 43 #include "../Core/RestApi/RestApi.h"
44 #include "../Core/Uuid.h" 44 #include "../Core/Uuid.h"
45 #include "../Core/OrthancException.h" 45 #include "../Core/OrthancException.h"
46 #include "../Core/Compression/ZlibCompressor.h" 46 #include "../Core/Compression/ZlibCompressor.h"
47 #include "../Core/RestApi/RestApiHierarchy.h" 47 #include "../Core/RestApi/RestApiHierarchy.h"
48 #include "../Core/HttpServer/HttpContentNegociation.h"
48 49
49 using namespace Orthanc; 50 using namespace Orthanc;
50 51
51 #if !defined(UNIT_TESTS_WITH_HTTP_CONNEXIONS) 52 #if !defined(UNIT_TESTS_WITH_HTTP_CONNEXIONS)
52 #error "Please set UNIT_TESTS_WITH_HTTP_CONNEXIONS" 53 #error "Please set UNIT_TESTS_WITH_HTTP_CONNEXIONS"
339 } 340 }
340 341
341 342
342 343
343 344
344 namespace Orthanc
345 {
346 class HttpContentNegociation : public boost::noncopyable
347 {
348 public:
349 typedef std::map<std::string, std::string> HttpHeaders;
350
351 class IHandler : public boost::noncopyable
352 {
353 public:
354 virtual ~IHandler()
355 {
356 }
357
358 virtual void Handle(const std::string& type,
359 const std::string& subtype) = 0;
360 };
361
362 private:
363 struct Handler
364 {
365 std::string type_;
366 std::string subtype_;
367 IHandler& handler_;
368
369 Handler(const std::string& type,
370 const std::string& subtype,
371 IHandler& handler) :
372 type_(type),
373 subtype_(subtype),
374 handler_(handler)
375 {
376 }
377
378 bool IsMatch(const std::string& type,
379 const std::string& subtype) const
380 {
381 if (type == "*" && subtype == "*")
382 {
383 return true;
384 }
385
386 if (subtype == "*" && type == type_)
387 {
388 return true;
389 }
390
391 return type == type_ && subtype == subtype_;
392 }
393
394 void Call() const
395 {
396 handler_.Handle(type_, subtype_);
397 }
398 };
399
400
401 struct Reference : public boost::noncopyable
402 {
403 const Handler& handler_;
404 uint8_t level_;
405 float quality_;
406
407 Reference(const Handler& handler,
408 const std::string& type,
409 const std::string& subtype,
410 float quality) :
411 handler_(handler),
412 quality_(quality)
413 {
414 if (type == "*" && subtype == "*")
415 {
416 level_ = 0;
417 }
418 else if (subtype == "*")
419 {
420 level_ = 1;
421 }
422 else
423 {
424 level_ = 2;
425 }
426 }
427
428 bool operator< (const Reference& other) const
429 {
430 if (level_ < other.level_)
431 {
432 return true;
433 }
434
435 if (level_ > other.level_)
436 {
437 return false;
438 }
439
440 return quality_ < other.quality_;
441 }
442 };
443
444
445 typedef std::vector<std::string> Tokens;
446 typedef std::list<Handler> Handlers;
447
448 Handlers handlers_;
449
450
451 static bool SplitPair(std::string& first /* out */,
452 std::string& second /* out */,
453 const std::string& source,
454 char separator)
455 {
456 size_t pos = source.find(separator);
457
458 if (pos == std::string::npos)
459 {
460 return false;
461 }
462 else
463 {
464 first = Toolbox::StripSpaces(source.substr(0, pos));
465 second = Toolbox::StripSpaces(source.substr(pos + 1));
466 return true;
467 }
468 }
469
470
471 static float GetQuality(const Tokens& parameters)
472 {
473 for (size_t i = 1; i < parameters.size(); i++)
474 {
475 std::string key, value;
476 if (SplitPair(key, value, parameters[i], '=') &&
477 key == "q")
478 {
479 float quality;
480 bool ok = false;
481
482 try
483 {
484 quality = boost::lexical_cast<float>(value);
485 ok = (quality >= 0.0f && quality <= 1.0f);
486 }
487 catch (boost::bad_lexical_cast&)
488 {
489 }
490
491 if (ok)
492 {
493 return quality;
494 }
495 else
496 {
497 LOG(ERROR) << "Quality parameter out of range in a HTTP request (must be between 0 and 1): " << value;
498 throw OrthancException(ErrorCode_BadRequest);
499 }
500 }
501 }
502
503 return 1.0f; // Default quality
504 }
505
506
507 static void SelectBestMatch(std::auto_ptr<Reference>& best,
508 const Handler& handler,
509 const std::string& type,
510 const std::string& subtype,
511 float quality)
512 {
513 std::auto_ptr<Reference> match(new Reference(handler, type, subtype, quality));
514
515 if (best.get() == NULL ||
516 *best < *match)
517 {
518 best = match;
519 }
520 }
521
522
523 public:
524 void Register(const std::string& mime,
525 IHandler& handler)
526 {
527 std::string type, subtype;
528
529 if (SplitPair(type, subtype, mime, '/') &&
530 type != "*" &&
531 subtype != "*")
532 {
533 handlers_.push_back(Handler(type, subtype, handler));
534 }
535 else
536 {
537 throw OrthancException(ErrorCode_ParameterOutOfRange);
538 }
539 }
540
541
542 bool Apply(const HttpHeaders& headers)
543 {
544 HttpHeaders::const_iterator accept = headers.find("accept");
545 if (accept != headers.end())
546 {
547 return Apply(accept->second);
548 }
549 else
550 {
551 return Apply("*/*");
552 }
553 }
554
555
556 bool Apply(const std::string& accept)
557 {
558 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
559 // https://en.wikipedia.org/wiki/Content_negotiation
560 // http://www.newmediacampaigns.com/blog/browser-rest-http-accept-headers
561
562 Tokens mediaRanges;
563 Toolbox::TokenizeString(mediaRanges, accept, ',');
564
565 std::auto_ptr<Reference> bestMatch;
566
567 for (Tokens::const_iterator it = mediaRanges.begin();
568 it != mediaRanges.end(); ++it)
569 {
570 Tokens parameters;
571 Toolbox::TokenizeString(parameters, *it, ';');
572
573 if (parameters.size() > 0)
574 {
575 float quality = GetQuality(parameters);
576
577 std::string type, subtype;
578 if (SplitPair(type, subtype, parameters[0], '/'))
579 {
580 for (Handlers::const_iterator it2 = handlers_.begin();
581 it2 != handlers_.end(); ++it2)
582 {
583 if (it2->IsMatch(type, subtype))
584 {
585 SelectBestMatch(bestMatch, *it2, type, subtype, quality);
586 }
587 }
588 }
589 }
590 }
591
592 if (bestMatch.get() == NULL) // No match was found
593 {
594 return false;
595 }
596 else
597 {
598 bestMatch->handler_.Call();
599 return true;
600 }
601 }
602 };
603 }
604
605
606 345
607 namespace 346 namespace
608 { 347 {
609 class AcceptHandler : public Orthanc::HttpContentNegociation::IHandler 348 class AcceptHandler : public Orthanc::HttpContentNegociation::IHandler
610 { 349 {