22from amaranth .lib .wiring import In , Out , Component , connect
33
44from printer import Printer
5+ from parse_start import ParseStart
56from stream_mux import StreamMux
67from stream_demux import StreamDemux
78from string_match import StringMatch
@@ -43,14 +44,11 @@ def elaborate(self, _platform):
4344 parser_demux = m .submodules .parser_demux = StreamDemux (mux_width = 4 , stream_width = 8 )
4445 connect (m , self .session .inbound .data , parser_demux .input )
4546
46- # Match the header for an HTTP/1.0 request to the LED path.
47- # TODO: If we want to match more than one path, could probably have some common
48- # matching for the method and protocol. Also, if we want to get out of the
49- # stone age, this could be HTTP/1.1.
50- led_start = "POST /led HTTP/1.0\r \n "
51- led_start_matcher = m .submodules .led_start_matcher = StringMatch (led_start )
52- HTTP_PARSER_LED = 0
53- connect (m , led_start_matcher .input , parser_demux .outs [HTTP_PARSER_LED ])
47+ # TODO: #4 - Add packet count and RFC2324 endpoints
48+ MATCHED_LED_PATH = 1 # start_matcher path match is in the order the paths are connected.
49+ start_matcher = m .submodules .start_matcher = ParseStart (["/led" ])
50+ HTTP_PARSER_START = 0
51+ connect (m , start_matcher .input , parser_demux .outs [HTTP_PARSER_START ])
5452
5553 HTTP_PARSER_HEADERS = 1
5654 skip_headers = m .submodules .end_of_header_matcher = StringContainsMatch ("\r \n \r \n " )
@@ -70,7 +68,7 @@ def elaborate(self, _platform):
7068 m .d .comb += parser_demux .outs [HTTP_PARSER_SINK ].ready .eq (1 )
7169
7270 ## Responders
73- response_mux = m .submodules .response_mux = StreamMux (mux_width = 2 , stream_width = 8 )
71+ response_mux = m .submodules .response_mux = StreamMux (mux_width = 3 , stream_width = 8 )
7472 connect (m , response_mux .out , self .session .outbound .data )
7573
7674 ok_response = "\r \n " .join (
@@ -107,46 +105,67 @@ def elaborate(self, _platform):
107105 not_found_printer .en .eq (1 ),
108106 ]
109107
108+ not_allowed_response = "\r \n " .join (
109+ ["HTTP/1.0 405 Method Not Allowed" ,
110+ "Host: Fomu" ,
111+ "Content-Type: text/plain; charset=utf-8" ,
112+ "" ,
113+ "" ,
114+ '🛑' ]) + "\r \n "
115+ not_allowed_response = not_allowed_response .encode ("utf-8" )
116+ not_allowed_printer = m .submodules .not_allowed_printer = Printer (not_allowed_response )
117+ RESPONSE_405 = 2
118+ connect (m , not_allowed_printer .output , response_mux .input [RESPONSE_405 ])
119+ send_405 = [
120+ response_mux .select .eq (RESPONSE_405 ),
121+ parser_demux .select .eq (HTTP_PARSER_SINK ),
122+ not_allowed_printer .en .eq (1 ),
123+ ]
124+
125+
110126 with m .FSM ():
111127 with m .State ("reset" ):
112128 m .d .comb += [
113- led_start_matcher .reset .eq (1 ),
129+ start_matcher .reset .eq (1 ),
114130 skip_headers .reset .eq (1 ),
115131 led_body_handler .reset .eq (1 ),
116132 ]
117133 m .next = "idle"
118134 with m .State ("idle" ):
119- m .d .comb += led_start_matcher .reset .eq (0 )
120- m .d .sync += parser_demux .select .eq (HTTP_PARSER_LED )
135+ m .d .comb += start_matcher .reset .eq (0 )
136+ m .d .sync += parser_demux .select .eq (HTTP_PARSER_START )
121137 m .d .sync += response_mux .select .eq (RESPONSE_OK )
122138 m .next = "idle"
123139 with m .If (self .session .inbound .active ):
124140 m .next = "parsing_start"
125141 m .d .sync += self .session .outbound .active .eq (1 )
126142 with m .State ("parsing_start" ):
127143 m .next = "parsing_start"
128- # Input finished before header matched, or header failed to match
129- with m .If (~ self .session .inbound .active | led_start_matcher .rejected ):
130- m .next = "writing"
131- m .d .sync += send_404
132144 # start line matched successfully
133- with m .Elif (led_start_matcher .accepted ):
134-
145+ with m .If (start_matcher .done ):
135146 m .next = "parsing_header"
136147 m .d .sync += parser_demux .select .eq (HTTP_PARSER_HEADERS )
137148 with m .State ("parsing_header" ):
138149 m .next = "parsing_header"
139150 with m .If (skip_headers .accepted ):
140- m .next = "parsing_body"
141- # TODO: #4 - Should pick the body parser based on the method+path from start
142- m .d .sync += parser_demux .select .eq (HTTP_PARSER_LED_BODY )
151+ with m .If (start_matcher .method [start_matcher .METHOD_POST ] &
152+ start_matcher .path [MATCHED_LED_PATH ]):
153+ m .next = "parsing_led_body"
154+ m .d .sync += parser_demux .select .eq (HTTP_PARSER_LED_BODY )
155+ with m .Elif (start_matcher .method [start_matcher .METHOD_GET ] &
156+ start_matcher .path [MATCHED_LED_PATH ]):
157+ m .next = "writing"
158+ m .d .sync += send_405
159+ with m .Else ():
160+ m .next = "writing"
161+ m .d .sync += send_404
143162 with m .Elif (~ self .session .inbound .active ):
144163 m .next = "writing"
145164 # TODO: #4 - Should send a different error code besides 404 if the
146165 # headers fail to parse before end-of-session.
147166 m .d .sync += send_404
148- with m .State ("parsing_body " ): # TODO: #4 - Make the specific body depend on the path
149- m .next = "parsing_body "
167+ with m .State ("parsing_led_body " ): # TODO: #4 - Make body parsing state more generic.
168+ m .next = "parsing_led_body "
150169 with m .If (led_body_handler .accepted ):
151170 m .next = "writing"
152171 m .d .sync += send_ok
@@ -160,10 +179,12 @@ def elaborate(self, _platform):
160179 m .d .sync += [
161180 ok_printer .en .eq (0 ),
162181 not_found_printer .en .eq (0 ),
182+ not_allowed_printer .en .eq (0 ),
163183 self .session .outbound .active .eq (1 ),
164184 ]
165185 with m .If ( ((response_mux .select == RESPONSE_OK ) & ok_printer .done )
166- | ((response_mux .select == RESPONSE_404 ) & not_found_printer .done )):
186+ | ((response_mux .select == RESPONSE_404 ) & not_found_printer .done )
187+ | ((response_mux .select == RESPONSE_405 ) & not_allowed_printer .done )):
167188 m .d .sync += self .session .outbound .active .eq (0 )
168189 # Can finish writing before all the input is collected,
169190 # since a bad request migh trigger an early 404. Wait
0 commit comments