Wie viel C ++ - Code muss geschrieben werden, um den HTTP-Header für die Autorisierung mit easy_parser von RESTinio zu analysieren?


Wir entwickeln den kostenlosen und offenen RESTinio- HTTP-Server weiter, der in C ++ - Anwendungen eingebettet ist . C ++ - Vorlagen werden aktiv in der Implementierung von RESTinio verwendet, die wir hier regelmäßig diskutieren ( aktuelles Beispiel ).


Einer der Anwendungspunkte für C ++ - Vorlagenmagie war easy_parser, eine kleine Implementierung eines nach unten rekursiven Parsers, der auf PEG basiert . Easy_parser wurde letztes Jahr zu RESTinio hinzugefügt, um die Arbeit mit HTTP-Headern zu vereinfachen.


Wir haben bereits in einem früheren Artikel ein wenig über easy_parser gesprochen . Und heute möchte ich zeigen, wie easy_parser bei der Entwicklung von RESTinio verwendet wird. Durch Parsen des Inhalts des HTTP-Headers für die Autorisierung. Versuchen wir sozusagen, in die Eingeweide von RESTinio zu schauen.


Grammatikautorisierung


Die Authorization-Header-Struktur ist in RFC7235 wie folgt definiert:


authorization-field = "Authorization" ":" OWS credentials

credentials = auth-scheme [ 1*SP ( token68 / [ #auth-param ] ) ]

auth-scheme = token

auth-param = token BWS "=" BWS ( token / quoted-string )

token68 = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="

Jene. Der Wert des Autorisierungsheaders muss den erforderlichen Namen des Authentifizierungsschemas und optionale Parameter für dieses Schema enthalten.


, token68 ( , base64), "=".


Authorization easy_parser?


?


easy_parser , , .


, , :


struct authorization_value_t
{
   enum class value_form_t { token, quoted_string };

   struct param_value_t
   {
      std::string value;
      value_form_t form;
   };

   struct param_t
   {
      std::string name;
      param_value_t value;
   };

   using param_container_t = std::vector< param_t >;

   struct token68_t
   {
      std::string value;
   };

   using auth_param_t = variant_t< token68_t, param_container_t >;

   std::string auth_scheme;
   auth_param_t auth_param;
};

authorization_value_t : auth_scheme auth_params. auth_paramstoken68_t, , param_t.


Authorization


producer


, , . ( ) , , :


auto make_parser()
{
   ... //   ,    ,  .

   return produce< authorization_value_t >(
         token_p() >> to_lower() >> &authorization_value_t::auth_scheme,
         maybe(
            repeat( 1, N, space() ),
            produce< auth_param_t >(
                  alternatives( token68_seq, params_seq )
            ) >> &authorization_value_t::auth_param
         )
   );
}

make_parser -, authorization_value_t. produce, . produce , C++ DSL.


:


  • token ( RFC7230). token- auth_scheme authorization_value_t;
  • , , , maybe;
  • ;
  • token68, . , auth_param authorization_value_t auth_param_t.

- . , , easy_parser DSL.


, - , , token68_seq params_seq . , ;)


token68_seq?


token68_seq — , make_parser :


auto token68_seq = sequence(
      token68_p() >> as_result(),
      not_clause( any_symbol_p() >> skip() ) );

, token68_seq — . token68 , , .


, , token68 . PEG, not-predicate. Not-pedicate , , . , not-predicate, .


, RFC7617 , token68, . , token68 . , .


token68_seq , token68_p . :


struct is_token68_char_predicate_t
   : protected hfp_impl::is_alphanum_predicate_t
{
   using base_type_t = hfp_impl::is_alphanum_predicate_t;

   bool operator()( const char actual ) const noexcept
   {
      return base_type_t::operator()(actual)
            || '-' == actual
            || '.' == actual
            || '_' == actual
            || '~' == actual
            || '+' == actual
            || '/' == actual
            ;
   }
};

inline auto token68_symbol_p()
{
   return restinio::easy_parser::impl::symbol_producer_template_t<
         is_token68_char_predicate_t >{};
}

inline auto token68_p()
{
   return produce< token68_t >(
         produce< std::string >(
            repeat( 1, N, token68_symbol_p() >> to_container() ),
            repeat( 0, N, symbol_p('=') >> to_container() )
         ) >> &token68_t::value
      );
}

, token68_p. is_token68_char_predicate_t token68_symbol_p — . , easy_parser , - , : digit_p, space_p, hexdigit_p, alpha_symbol_p, alphanum_symbol_p .. , , token68, . is_token68_char_predicate_t token68_symbol_p .


token68_p token68 -- : token68, =. .


params_seq?


, params_seq. , , make_parser, :


auto make_parser()
{
   auto token_to_v = []( std::string v ) -> param_value_t {
      return { std::move(v), value_form_t::token };
   };
   auto qstring_to_v = []( std::string v ) -> param_value_t {
      return { std::move(v), value_form_t::quoted_string };
   };

   auto token68_seq = sequence(
         token68_p() >> as_result(),
         not_clause( any_symbol_p() >> skip() ) );

   //    name=value   .
   auto params_seq = maybe_empty_comma_separated_list_p< param_container_t >(
         produce< param_t >(
            token_p() >> to_lower() >> &param_t::name,
            ows(),
            symbol('='),
            ows(),
            produce< param_value_t >(
               alternatives(
                  token_p() >> convert( token_to_v ) >> as_result(),
                  quoted_string_p() >> convert( qstring_to_v )
                        >> as_result()
               )
            ) >> &param_t::value
         )
      ) >> as_result();

   return produce< authorization_value_t >(
         token_p() >> to_lower() >> &authorization_value_t::auth_scheme,
         maybe(
            repeat( 1, N, space() ),
            produce< auth_param_t >(
                  alternatives( token68_seq, params_seq )
            ) >> &authorization_value_t::auth_param
         )
   );
}

, lambda- token_to_v qstring_to_v. , std::string param_value_t, : token quoted-string.


- , name=value std::string, param_value_t, std::string .


params_seq:


auto params_seq = maybe_empty_comma_separated_list_p< param_container_t >(
      ...
   ) >> as_result();

params_seq , param_container_t. , .


maybe_empty_comma_separated_list_p easy_parser- , RFC HTTP #auth-param, # :


[ ( "," / auth-param )  *( OWS "," [ OWS auth-param ] ) ]

params_seq , . auth-param. , -, :


produce< param_t >(
   token_p() >> to_lower() >> &param_t::name,
   ows(),
   symbol('='),
   ows(),
   produce< param_value_t >(
      alternatives(
         token_p() >> convert( token_to_v ) >> as_result(),
         quoted_string_p() >> convert( qstring_to_v )
               >> as_result()
      )
   ) >> &param_t::value
)

, param_t, :


  • token, param_t::name;
  • ( ows OWS (.. optional whitespace) RFC7230);
  • =;
  • ;
  • (.. param_value_t), : token, quoted-string. token, , token_to_v ( ) . quoted-string, , qstring_to_v;
  • param_value_t param_t::value.

, , .



" ?" .



, , , , RESTinio , . , , RESTinio.


, . , RESTinio, , , C++ . , .


easy_parser RESTinio , RESTinio HTTP-.


, - , - . ( , ), - .


, - ( bison, coco/r, ragel .) . RESTinio header-only ( libhttp-parser). - header-only . , : , - RESTinio, , - ( , RESTinio)? ragel coco/r? , .


, C++ DSL. , RESTinio - Boost- - PEGTL , .. . , (Boost.Spirit PEGTL) , . , show stopper-. easy_parser.


C HTTP- RESTinio easy_parser-. .


...


, easy_parser, . . , a) RFC, b) , , , c) d) . , , - 4-6 , easy_parser- — 15-20 .


, easy_parser- RESTinio , - RESTinio .


RESTinio


Die zweite Antwort auf die Frage "Warum und worüber wurde dieser Artikel geschrieben?" nur trivial und offensichtlich: Ich möchte noch einmal daran erinnern, dass das Projekt lebt und sich entwickelt. Sie können es nehmen, ausprobieren und dann Ihre Gefühle mit uns teilen: Was Ihnen gefallen hat, was Ihnen nicht gefallen hat, was gefehlt hat, was nicht so gemacht wurde, wie Sie es möchten ... Wir hören uns alle konstruktiven Kommentare und Vorschläge an (ich denke, dass einige Leser dies bestätigen können )


All Articles