OSDN Git Service

7b9a81cb6e95e9dfc12ebee500f320762262a0ef
[mimic/MiMicSDK.git] / lib / src / net / httpd / NyLPC_cHttpdConnection.c
1 #include "NyLPC_cHttpdConnection_protected.h"\r
2 #include "NyLPC_http.h"\r
3 #include "NyLPC_netif.h"\r
4 #include "NyLPC_cHttpdUtils.h"\r
5 #include "./NyLPC_cHttpd_protected.h"\r
6 \r
7 \r
8 \r
9 NyLPC_TBool NyLPC_cHttpdConnection_initialize(NyLPC_TcHttpdConnection_t* i_inst,NyLPC_TcHttpd_t* i_parent_httpd)\r
10 {\r
11     i_inst->_socket=NyLPC_cNet_createTcpSocketEx(NyLPC_TSocketType_TCP_HTTP);\r
12     if(i_inst->_socket==NULL){\r
13         return NyLPC_TBool_FALSE;\r
14     }\r
15     NyLPC_cHttpRequestPrefixParser_initialize(&(i_inst->_pparser));\r
16     i_inst->_parent_httpd=i_parent_httpd;\r
17     i_inst->_res_status=NyLPC_cHttpdConnection_ResStatus_CLOSED;\r
18     i_inst->_req_status=NyLPC_cHttpdConnection_ReqStatus_LISTEN;\r
19     return NyLPC_TBool_TRUE;\r
20 }\r
21 \r
22 void NyLPC_cHttpdConnection_finalize(NyLPC_TcHttpdConnection_t* i_inst)\r
23 {\r
24     NyLPC_cHttpdConnection_closeResponse(i_inst);\r
25     NyLPC_cHttpdConnection_closeSocket(i_inst);\r
26     NyLPC_cHttpRequestPrefixParser_finalize(i_inst);\r
27     NyLPC_iTcpSocket_finalize(i_inst->_socket);\r
28 }\r
29 \r
30 const NyLPC_TChar* NyLPC_cHttpdConnection_getUrlPrefix(const NyLPC_TcHttpdConnection_t* i_inst)\r
31 {\r
32     return NyLPC_cHttpRequestPrefixParser_getUrlPrefix(&i_inst->_pparser);\r
33 }\r
34 void NyLPC_cHttpdConnection_setReqStatusParsed(NyLPC_TcHttpdConnection_t* i_inst)\r
35 {\r
36     i_inst->_req_status=NyLPC_cHttpdConnection_ReqStatus_END;\r
37 }\r
38 \r
39 #define NyLPC_cHttpHeaderWriter_CONTENT_LENGTH_UNLIMITED 0xFFFFFFFF\r
40 \r
41 \r
42 NyLPC_TBool NyLPC_cHttpdConnection_send100Continue(NyLPC_TcHttpdConnection_t* i_inst)\r
43 {\r
44     //状態の確認\r
45     if(i_inst->_res_status!=NyLPC_cHttpdConnection_ResStatus_HEAD)\r
46     {\r
47         NyLPC_OnErrorGoto(Error_Status);\r
48     }\r
49     //ステータスラインの記述\r
50     if(!NyLPC_iHttpPtrStream_write(&(i_inst->_in_stream.super),"HTTP/1.1 100 Continue\r\n\r\n",25)){\r
51         NyLPC_OnErrorGoto(Error);\r
52     }\r
53     return NyLPC_TBool_TRUE;\r
54 Error:\r
55 Error_Status:\r
56     i_inst->_res_status=NyLPC_cHttpdConnection_ResStatus_ERROR;\r
57     return NyLPC_TBool_FALSE;\r
58 }\r
59 \r
60 /**\r
61  * レスポンスヘッダを送信します。\r
62  */\r
63 NyLPC_TBool NyLPC_cHttpdConnection_sendResponseHeader(NyLPC_TcHttpdConnection_t* i_inst,NyLPC_TUInt16 i_response_code,const NyLPC_TChar* i_content_type,const NyLPC_TChar* i_additional_header)\r
64 {\r
65     return NyLPC_cHttpdConnection_sendResponseHeader2(i_inst,i_response_code,i_content_type,NyLPC_cHttpHeaderWriter_CONTENT_LENGTH_UNLIMITED,i_additional_header);\r
66 }\r
67 \r
68 NyLPC_TBool NyLPC_cHttpdConnection_sendResponseHeader2(NyLPC_TcHttpdConnection_t* i_inst,NyLPC_TUInt16 i_response_code,const NyLPC_TChar* i_content_type,NyLPC_TUInt32 i_content_length,const NyLPC_TChar* i_additional_header)\r
69 {\r
70     NyLPC_TcHttpHeaderWriter_t* h=&(i_inst->_head_writer);\r
71     //状態の確認\r
72     if(i_inst->_res_status!=NyLPC_cHttpdConnection_ResStatus_HEAD)\r
73     {\r
74         NyLPC_OnErrorGoto(Error_Status);\r
75     }\r
76     //ヘッダ送信\r
77     if(!NyLPC_cHttpHeaderWriter_initialize(h,&(i_inst->_in_stream.super),NULL)){\r
78         NyLPC_OnErrorGoto(ERROR_SEND);\r
79     }\r
80     //Headerの転送モードセット\r
81     if(i_content_length==NyLPC_cHttpHeaderWriter_CONTENT_LENGTH_UNLIMITED){\r
82         NyLPC_cHttpHeaderWriter_setChunked(h);\r
83     }else{\r
84         NyLPC_cHttpHeaderWriter_setContentLength(h,i_content_length);\r
85     }\r
86     //continueにセットされていたらcloseをFALSEに\r
87     NyLPC_cHttpHeaderWriter_setConnectionClose(h,(i_inst->_connection_message_mode!=NyLPC_TcHttpdConnection_CONNECTION_MODE_CONTINUE));\r
88 \r
89     if(!NyLPC_cHttpHeaderWriter_writeResponseHeader(h,i_response_code)){\r
90         NyLPC_OnErrorGoto(ERROR_SEND);\r
91     }\r
92     if(!NyLPC_cHttpHeaderWriter_writeMessage(h,"Content-type",i_content_type)){\r
93         NyLPC_OnErrorGoto(ERROR_SEND);\r
94     }\r
95     if(i_additional_header!=NULL){\r
96         if(!NyLPC_cHttpHeaderWriter_writeRawMessage(h,i_additional_header)){\r
97             NyLPC_OnErrorGoto(ERROR_SEND);\r
98         }\r
99     }\r
100     NyLPC_cHttpHeaderWriter_close(h);\r
101     NyLPC_cHttpHeaderWriter_finalize(h);\r
102     i_inst->_res_status=NyLPC_cHttpdConnection_ResStatus_BODY;\r
103     //BodyWriter生成\r
104     NyLPC_cHttpBodyWriter_initialize(&(i_inst->_body_writer),&(i_inst->_in_stream));\r
105     //bodyのchunkedもセット\r
106     if(i_content_length==NyLPC_cHttpHeaderWriter_CONTENT_LENGTH_UNLIMITED){\r
107         NyLPC_cHttpBodyWriter_setChunked(&(i_inst->_body_writer));\r
108     }else{\r
109         NyLPC_cHttpBodyWriter_setContentLength(&(i_inst->_body_writer),i_content_length);\r
110     }\r
111     return NyLPC_TBool_TRUE;\r
112 ERROR_SEND:\r
113     NyLPC_cHttpHeaderWriter_finalize(&(i_inst->_head_writer));\r
114 Error_Status:\r
115     i_inst->_res_status=NyLPC_cHttpdConnection_ResStatus_ERROR;\r
116     return NyLPC_TBool_FALSE;\r
117 }\r
118 \r
119 \r
120 /**\r
121  * レスポンスBodyを送信します。\r
122  * 関数を実行後、_res_statusはBODYかERRORに遷移します。\r
123  */\r
124 NyLPC_TBool NyLPC_cHttpdConnection_sendResponseBody(NyLPC_TcHttpdConnection_t* i_inst,const void* i_data,NyLPC_TUInt32 i_size)\r
125 {\r
126     if(i_inst->_res_status!=NyLPC_cHttpdConnection_ResStatus_BODY)\r
127     {\r
128         NyLPC_OnErrorGoto(Error);\r
129     }\r
130     //Bodyの書込み\r
131     if(!NyLPC_cHttpBodyWriter_write(&(i_inst->_body_writer),i_data,i_size)){\r
132         NyLPC_OnErrorGoto(Error_Send);\r
133     }\r
134     return NyLPC_TBool_TRUE;\r
135 Error_Send:\r
136     NyLPC_cHttpBodyWriter_finalize(&(i_inst->_in_stream));\r
137 Error:\r
138     i_inst->_res_status=NyLPC_cHttpdConnection_ResStatus_ERROR;\r
139     return NyLPC_TBool_FALSE;\r
140 }\r
141 /**\r
142  * レスポンスBodyを書式出力して送信します。\r
143  * 関数を実行後、_res_statusはBODYかERRORに遷移します。\r
144  */\r
145 NyLPC_TBool NyLPC_cHttpdConnection_sendResponseBodyF(NyLPC_TcHttpdConnection_t* i_inst,const char* i_fmt,...)\r
146 {\r
147     va_list a;\r
148     if(i_inst->_res_status!=NyLPC_cHttpdConnection_ResStatus_BODY)\r
149     {\r
150         NyLPC_OnErrorGoto(Error);\r
151     }\r
152     //Bodyの書込み\r
153     va_start(a,i_fmt);\r
154     if(!NyLPC_cHttpBodyWriter_formatV(&(i_inst->_body_writer),i_fmt,a)){\r
155         NyLPC_OnErrorGoto(Error_Send);\r
156     }\r
157     va_end(a);\r
158     return NyLPC_TBool_TRUE;\r
159 Error_Send:\r
160     va_end(a);\r
161     NyLPC_cHttpBodyWriter_finalize(&(i_inst->_in_stream));\r
162 Error:\r
163     i_inst->_res_status=NyLPC_cHttpdConnection_ResStatus_ERROR;\r
164     return NyLPC_TBool_FALSE;\r
165 }\r
166 \r
167 /**\r
168  * ヘッダのみのErrorレスポンスを送信する。\r
169  * この関数はワーク用のHeaderWriterを使います。\r
170  */\r
171 static void sendErrorResponse(NyLPC_TcHttpdConnection_t* i_inst,NyLPC_TInt16 i_status)\r
172 {\r
173     NyLPC_TcHttpHeaderWriter_t* h=&(i_inst->_head_writer);\r
174     if(NyLPC_cHttpHeaderWriter_initialize(h,&i_inst->_in_stream.super,NULL)){\r
175         //ヘッダを送信\r
176         NyLPC_cHttpHeaderWriter_setConnectionClose(h,NyLPC_TBool_TRUE);\r
177         NyLPC_cHttpHeaderWriter_writeResponseHeader(h,i_status);\r
178         NyLPC_cHttpHeaderWriter_close(h);\r
179         NyLPC_cHttpHeaderWriter_finalize(h);\r
180     }\r
181 }\r
182 /**\r
183  * 関数を実行後、_res_statusはCLOSEDかHEADかERRORに遷移する。\r
184  */\r
185 NyLPC_TBool NyLPC_cHttpdConnection_closeResponse(NyLPC_TcHttpdConnection_t* i_inst)\r
186 {\r
187     NyLPC_TcHttpBodyWriter_t* b;\r
188     switch(i_inst->_res_status){\r
189     case NyLPC_cHttpdConnection_ResStatus_CLOSED:\r
190     case NyLPC_cHttpdConnection_ResStatus_ERROR:\r
191         //何もせずにコネクションをクローズする。\r
192         return NyLPC_TBool_FALSE;\r
193     case NyLPC_cHttpdConnection_ResStatus_HEAD:\r
194         //エラー500を送信してクローズする。\r
195         sendErrorResponse(i_inst,500);\r
196         i_inst->_res_status=NyLPC_cHttpdConnection_ResStatus_CLOSED;\r
197         return NyLPC_TBool_FALSE;\r
198     case NyLPC_cHttpdConnection_ResStatus_BODY:\r
199         //正常終了。BODYをクローズし、終了する。\r
200         b=&(i_inst->_body_writer);\r
201         NyLPC_cHttpBodyWriter_close(b);\r
202         NyLPC_cHttpBodyWriter_finalize(&b);\r
203         i_inst->_res_status=NyLPC_cHttpdConnection_ResStatus_HEAD;\r
204         if(i_inst->_connection_message_mode!=NyLPC_TcHttpdConnection_CONNECTION_MODE_CONTINUE)\r
205         {\r
206             i_inst->_res_status=NyLPC_cHttpdConnection_ResStatus_CLOSED;\r
207             return NyLPC_TBool_FALSE;\r
208         }\r
209         return NyLPC_TBool_TRUE;\r
210     default:\r
211         NyLPC_Abort();\r
212     }\r
213     return NyLPC_TBool_TRUE;\r
214 }\r
215 \r
216 /**\r
217  * コネクションのプリフェッチデータをヘッダパーサへpushします。\r
218  */\r
219 NyLPC_TBool NyLPC_cHttpdConnection_pushPrefetchInfo(NyLPC_TcHttpdConnection_t* i_inst,NyLPC_TcHttpBasicHeaderParser_t* i_header_parser,struct NyLPC_THttpBasicHeader* o_out)\r
220 {\r
221     const char* method=NyLPC_THttpMethodType_toString(i_inst->_pparser.method);\r
222     if(NyLPC_cHttpBasicHeaderParser_parseChar(i_header_parser,method,strlen(method),o_out)<0){\r
223         NyLPC_OnErrorGoto(Error);\r
224     }\r
225     if(NyLPC_cHttpBasicHeaderParser_parseChar(i_header_parser," ",1,o_out)<0){\r
226         NyLPC_OnErrorGoto(Error);\r
227     }\r
228     if(NyLPC_cHttpBasicHeaderParser_parseChar(i_header_parser,i_inst->_pparser._url,strlen(i_inst->_pparser._url),o_out)<0){\r
229         NyLPC_OnErrorGoto(Error);\r
230     }\r
231     return NyLPC_TBool_TRUE;\r
232 Error:\r
233     return NyLPC_TBool_FALSE;\r
234 }\r
235 \r
236 #define NyLPC_cHttpdConnection_TIMEOUT_ACCEPT   3000\r
237 #define NyLPC_cHttpdConnection_TIMEOUT_CLOSE    5000\r
238 #define NyLPC_cHttpdConnection_TIMEOUT_LISTEN   5000\r
239 \r
240 \r
241 /**\r
242  * listenerでConnectionのソケットに接続を待ちます。\r
243  */\r
244 NyLPC_TBool NyLPC_cHttpdConnection_listenSocket(NyLPC_TcHttpdConnection_t* i_inst,NyLPC_TiTcpListener_t* i_listener)\r
245 {\r
246     NyLPC_Assert(i_inst->_req_status==NyLPC_cHttpdConnection_ReqStatus_LISTEN);\r
247     //リスニング\r
248     if(!NyLPC_iTcpListener_listen(i_listener,i_inst->_socket,NyLPC_cHttpdConnection_TIMEOUT_LISTEN)){\r
249         return NyLPC_TBool_FALSE;\r
250     }\r
251     //成功したらステータス遷移\r
252     i_inst->_req_status=NyLPC_cHttpdConnection_ReqStatus_ACCEPT;\r
253     return NyLPC_TBool_TRUE;\r
254 }\r
255 \r
256 /**\r
257  * コネクションのソケットをacceptします。\r
258  */\r
259 NyLPC_TBool NyLPC_cHttpdConnection_acceptSocket(NyLPC_TcHttpdConnection_t* i_inst)\r
260 {\r
261     NyLPC_Assert(i_inst->_req_status==NyLPC_cHttpdConnection_ReqStatus_ACCEPT);\r
262 \r
263     if(!NyLPC_iTcpSocket_accept(i_inst->_socket,NyLPC_cHttpdConnection_TIMEOUT_ACCEPT)){\r
264         NyLPC_OnErrorGoto(Error);\r
265     }\r
266     //HttpStreamの生成\r
267     if(!NyLPC_cHttpStream_initialize(&i_inst->_in_stream,i_inst->_socket)){\r
268         NyLPC_OnErrorGoto(Error_Connected);\r
269     }\r
270     //初回だけHEADに遷移\r
271     i_inst->_res_status=NyLPC_cHttpdConnection_ResStatus_HEAD;\r
272     i_inst->_req_status=NyLPC_cHttpdConnection_ReqStatus_PREFETCH;\r
273     i_inst->_connection_message_mode=NyLPC_TcHttpdConnection_CONNECTION_MODE_CLOSE;\r
274     return NyLPC_TBool_TRUE;\r
275 Error_Connected:\r
276     NyLPC_iTcpSocket_close(i_inst->_socket,NyLPC_cHttpdConnection_TIMEOUT_CLOSE);\r
277     i_inst->_req_status=NyLPC_cHttpdConnection_ReqStatus_LISTEN;\r
278 Error:\r
279     return NyLPC_TBool_FALSE;\r
280 }\r
281 \r
282 NyLPC_TBool NyLPC_cHttpdConnection_prefetch(NyLPC_TcHttpdConnection_t* i_inst)\r
283 {\r
284     NyLPC_Assert(i_inst->_req_status==NyLPC_cHttpdConnection_ReqStatus_PREFETCH);\r
285 \r
286     //Prefetchを実行\r
287     if(!NyLPC_cHttpRequestPrefixParser_parse(&i_inst->_pparser,&i_inst->_in_stream.super)){\r
288         //400エラー\r
289         sendErrorResponse(i_inst,400);\r
290         NyLPC_OnErrorGoto(Error_Prefetch);\r
291     }\r
292     i_inst->_req_status=NyLPC_cHttpdConnection_ReqStatus_REQPARSE;\r
293     return NyLPC_TBool_TRUE;\r
294 Error_Prefetch:\r
295     NyLPC_iTcpSocket_close(i_inst->_socket,NyLPC_cHttpdConnection_TIMEOUT_CLOSE);\r
296     i_inst->_req_status=NyLPC_cHttpdConnection_ReqStatus_LISTEN;\r
297     return NyLPC_TBool_FALSE;\r
298 }\r
299 \r
300 \r
301 \r
302 \r
303 \r
304 \r
305 \r
306 NyLPC_TBool NyLPC_cHttpdConnection_prevNextPrefetch(NyLPC_TcHttpdConnection_t* i_inst)\r
307 {\r
308     NyLPC_TcHttpNullRequestHeaderParser_t parser;\r
309     switch(i_inst->_req_status)\r
310     {\r
311     case NyLPC_cHttpdConnection_ReqStatus_REQPARSE:\r
312         //リクエストパース待ちなら前段のリクエストを吸収しておく。\r
313         NyLPC_cHttpNullRequestHeaderParser_initialize(&parser);\r
314         //プリフェッチしたデータを流す\r
315         NyLPC_cHttpNullRequestHeaderParser_parseInit(&parser);\r
316         NyLPC_cHttpNullRequestHeaderParser_parseChar(&parser,"GET ",4);//決め打ち\r
317         NyLPC_cHttpNullRequestHeaderParser_parseChar(&parser,i_inst->_pparser._url,strlen(i_inst->_pparser._url));\r
318         //後続をストリームから取り込む\r
319         if(NyLPC_cHttpNullRequestHeaderParser_parseStream(&parser,&(i_inst->_in_stream.super))){\r
320             if(NyLPC_cHttpNullRequestHeaderParser_parseFinish(&parser)){\r
321                 NyLPC_cHttpNullRequestHeaderParser_finalize(&parser);\r
322                 //OK:403\r
323                 sendErrorResponse(i_inst,403);\r
324                 break;//OK\r
325             }\r
326         }\r
327 \r
328         NyLPC_cHttpNullRequestHeaderParser_finalize(&parser);\r
329         //NG:400 Bad Request\r
330         sendErrorResponse(i_inst,400);\r
331         return NyLPC_TBool_FALSE;//吸収失敗\r
332     case NyLPC_cHttpdConnection_ReqStatus_END:\r
333         //リクエストがパース済みならprefetchに戻す。\r
334         i_inst->_req_status=NyLPC_cHttpdConnection_ReqStatus_PREFETCH;\r
335     default:\r
336         NyLPC_Abort();\r
337     }\r
338     //吸収成功\r
339     return NyLPC_TBool_TRUE;\r
340 }\r
341 \r
342 void NyLPC_cHttpdConnection_closeSocket(NyLPC_TcHttpdConnection_t* i_inst)\r
343 {\r
344     switch(i_inst->_req_status)\r
345     {\r
346     case NyLPC_cHttpdConnection_ReqStatus_LISTEN:\r
347         //何も出来ない。\r
348         break;\r
349     case NyLPC_cHttpdConnection_ReqStatus_END:\r
350     case NyLPC_cHttpdConnection_ReqStatus_REQPARSE:\r
351     case NyLPC_cHttpdConnection_ReqStatus_PREFETCH:\r
352         NyLPC_cHttpStream_finalize(&i_inst->_in_stream);\r
353     case NyLPC_cHttpdConnection_ReqStatus_ACCEPT:\r
354         NyLPC_iTcpSocket_close(i_inst->_socket,NyLPC_cHttpdConnection_TIMEOUT_CLOSE);\r
355     default:\r
356         break;\r
357     }\r
358     i_inst->_req_status=NyLPC_cHttpdConnection_ReqStatus_LISTEN;\r
359     i_inst->_res_status=NyLPC_cHttpdConnection_ResStatus_CLOSED;\r
360 }\r
361 \r
362 /**\r
363  * Httpd全体で唯一のロックを取得する。\r
364  */\r
365 void NyLPC_cHttpdConnection_lock(NyLPC_TcHttpdConnection_t* i_inst)\r
366 {\r
367     NyLPC_cHttpd_lock((NyLPC_TcHttpd_t*)(i_inst->_parent_httpd));\r
368 }\r
369 /**\r
370  * Httpd全体で唯一のロックを開放する。\r
371  */\r
372 void NyLPC_cHttpdConnection_unlock(NyLPC_TcHttpdConnection_t* i_inst)\r
373 {\r
374     NyLPC_cHttpd_unlock((NyLPC_TcHttpd_t*)(i_inst->_parent_httpd));\r
375 }\r