@@ -603,6 +603,192 @@ where
603603 }
604604}
605605
606+ /// Parse ACCEPT header, default to JSON if missing or mal-formatted
607+ pub fn get_accept_header ( req_headers : & HeaderMap ) -> Accept {
608+ Accept :: from_str (
609+ req_headers. get ( ACCEPT ) . and_then ( |value| value. to_str ( ) . ok ( ) ) . unwrap_or ( "application/json" ) ,
610+ )
611+ . unwrap_or ( Accept :: Json )
612+ }
613+
614+ /// Parse CONTENT TYPE header, default to JSON if missing or mal-formatted
615+ pub fn get_content_type_header ( req_headers : & HeaderMap ) -> ContentType {
616+ ContentType :: from_str (
617+ req_headers
618+ . get ( CONTENT_TYPE )
619+ . and_then ( |value| value. to_str ( ) . ok ( ) )
620+ . unwrap_or ( "application/json" ) ,
621+ )
622+ . unwrap_or ( ContentType :: Json )
623+ }
624+
625+ /// Parse CONSENSUS_VERSION header
626+ pub fn get_consensus_version_header ( req_headers : & HeaderMap ) -> Option < ForkName > {
627+ ForkName :: from_str (
628+ req_headers
629+ . get ( CONSENSUS_VERSION_HEADER )
630+ . and_then ( |value| value. to_str ( ) . ok ( ) )
631+ . unwrap_or ( "" ) ,
632+ )
633+ . ok ( )
634+ }
635+
636+ #[ derive( Debug , Clone , Copy , PartialEq ) ]
637+ pub enum ForkName {
638+ Deneb ,
639+ Electra ,
640+ }
641+
642+ impl std:: fmt:: Display for ForkName {
643+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
644+ match self {
645+ ForkName :: Deneb => write ! ( f, "deneb" ) ,
646+ ForkName :: Electra => write ! ( f, "electra" ) ,
647+ }
648+ }
649+ }
650+
651+ impl FromStr for ForkName {
652+ type Err = String ;
653+ fn from_str ( value : & str ) -> Result < Self , Self :: Err > {
654+ match value {
655+ "deneb" => Ok ( ForkName :: Deneb ) ,
656+ "electra" => Ok ( ForkName :: Electra ) ,
657+ _ => Err ( format ! ( "Invalid fork name {}" , value) ) ,
658+ }
659+ }
660+ }
661+
662+ #[ derive( Debug , Clone , Copy , PartialEq ) ]
663+ pub enum ContentType {
664+ Json ,
665+ Ssz ,
666+ }
667+
668+ impl std:: fmt:: Display for ContentType {
669+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
670+ match self {
671+ ContentType :: Json => write ! ( f, "application/json" ) ,
672+ ContentType :: Ssz => write ! ( f, "application/octet-stream" ) ,
673+ }
674+ }
675+ }
676+
677+ impl FromStr for ContentType {
678+ type Err = String ;
679+ fn from_str ( value : & str ) -> Result < Self , Self :: Err > {
680+ match value {
681+ "application/json" => Ok ( ContentType :: Json ) ,
682+ "application/octet-stream" => Ok ( ContentType :: Ssz ) ,
683+ _ => Ok ( ContentType :: Json ) ,
684+ }
685+ }
686+ }
687+
688+ #[ derive( Debug , Clone , Copy , PartialEq ) ]
689+ pub enum Accept {
690+ Json ,
691+ Ssz ,
692+ Any ,
693+ }
694+
695+ impl fmt:: Display for Accept {
696+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
697+ match self {
698+ Accept :: Ssz => write ! ( f, "application/octet-stream" ) ,
699+ Accept :: Json => write ! ( f, "application/json" ) ,
700+ Accept :: Any => write ! ( f, "*/*" ) ,
701+ }
702+ }
703+ }
704+
705+ impl FromStr for Accept {
706+ type Err = String ;
707+
708+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
709+ let media_type_list = MediaTypeList :: new ( s) ;
710+
711+ // [q-factor weighting]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2
712+ // find the highest q-factor supported accept type
713+ let mut highest_q = 0_u16 ;
714+ let mut accept_type = None ;
715+
716+ const APPLICATION : & str = names:: APPLICATION . as_str ( ) ;
717+ const OCTET_STREAM : & str = names:: OCTET_STREAM . as_str ( ) ;
718+ const JSON : & str = names:: JSON . as_str ( ) ;
719+ const STAR : & str = names:: _STAR. as_str ( ) ;
720+ const Q : & str = names:: Q . as_str ( ) ;
721+
722+ media_type_list. into_iter ( ) . for_each ( |item| {
723+ if let Ok ( MediaType { ty, subty, suffix : _, params } ) = item {
724+ let q_accept = match ( ty. as_str ( ) , subty. as_str ( ) ) {
725+ ( APPLICATION , OCTET_STREAM ) => Some ( Accept :: Ssz ) ,
726+ ( APPLICATION , JSON ) => Some ( Accept :: Json ) ,
727+ ( STAR , STAR ) => Some ( Accept :: Any ) ,
728+ _ => None ,
729+ }
730+ . map ( |item_accept_type| {
731+ let q_val = params
732+ . iter ( )
733+ . find_map ( |( n, v) | match n. as_str ( ) {
734+ Q => {
735+ Some ( ( v. as_str ( ) . parse :: < f32 > ( ) . unwrap_or ( 0_f32 ) * 1000_f32 ) as u16 )
736+ }
737+ _ => None ,
738+ } )
739+ . or ( Some ( 1000_u16 ) ) ;
740+
741+ ( q_val. unwrap ( ) , item_accept_type)
742+ } ) ;
743+
744+ match q_accept {
745+ Some ( ( q, accept) ) if q > highest_q => {
746+ highest_q = q;
747+ accept_type = Some ( accept) ;
748+ }
749+ _ => ( ) ,
750+ }
751+ }
752+ } ) ;
753+ accept_type. ok_or_else ( || "accept header is not supported" . to_string ( ) )
754+ }
755+ }
756+
757+ #[ must_use]
758+ #[ derive( Debug , Clone , Copy , Default ) ]
759+ pub struct JsonOrSsz < T > ( pub T ) ;
760+
761+ impl < T , S > FromRequest < S > for JsonOrSsz < T >
762+ where
763+ T : serde:: de:: DeserializeOwned + ssz:: Decode + ' static ,
764+ S : Send + Sync ,
765+ {
766+ type Rejection = Response ;
767+
768+ async fn from_request ( req : Request , _state : & S ) -> Result < Self , Self :: Rejection > {
769+ let headers = req. headers ( ) . clone ( ) ;
770+ let content_type = headers. get ( CONTENT_TYPE ) . and_then ( |value| value. to_str ( ) . ok ( ) ) ;
771+
772+ let bytes = Bytes :: from_request ( req, _state) . await . map_err ( IntoResponse :: into_response) ?;
773+
774+ if let Some ( content_type) = content_type {
775+ if content_type. starts_with ( & ContentType :: Json . to_string ( ) ) {
776+ let payload: T = serde_json:: from_slice ( & bytes)
777+ . map_err ( |_| StatusCode :: BAD_REQUEST . into_response ( ) ) ?;
778+ return Ok ( Self ( payload) ) ;
779+ }
780+
781+ if content_type. starts_with ( & ContentType :: Ssz . to_string ( ) ) {
782+ let payload = T :: from_ssz_bytes ( & bytes)
783+ . map_err ( |_| StatusCode :: BAD_REQUEST . into_response ( ) ) ?;
784+ return Ok ( Self ( payload) ) ;
785+ }
786+ }
787+
788+ Err ( StatusCode :: UNSUPPORTED_MEDIA_TYPE . into_response ( ) )
789+ }
790+ }
791+
606792#[ cfg( unix) ]
607793pub async fn wait_for_signal ( ) -> eyre:: Result < ( ) > {
608794 use tokio:: signal:: unix:: { SignalKind , signal} ;
0 commit comments