OSDN Git Service

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