OSDN Git Service

change I/F.
[bbk/bchanf.git] / src / http / http_connector.c
1 /*
2  * http_connector.c
3  *
4  * Copyright (c) 2012 project bchan
5  *
6  * This software is provided 'as-is', without any express or implied
7  * warranty. In no event will the authors be held liable for any damages
8  * arising from the use of this software.
9  *
10  * Permission is granted to anyone to use this software for any purpose,
11  * including commercial applications, and to alter it and redistribute it
12  * freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must not
15  *    claim that you wrote the original software. If you use this software
16  *    in a product, an acknowledgment in the product documentation would be
17  *    appreciated but is not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  *    misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source
23  *    distribution.
24  *
25  */
26
27 #include "http_connector.h"
28
29 #include        <basic.h>
30 #include        <bstdio.h>
31 #include        <bstdlib.h>
32 #include        <errcode.h>
33 #include        <btron/btron.h>
34 #include        <btron/bsocket.h>
35
36 #ifdef BCHAN_CONFIG_DEBUG
37 # define DP(arg) printf arg
38 # define DP_ER(msg, err) printf("%s (%d/%x)\n", msg, err>>16, err)
39 #else
40 # define DP(arg) /**/
41 # define DP_ER(msg, err) /**/
42 #endif
43
44 struct http_reqentry_t_ {
45         enum {
46                 NON_EXISTENT,
47                 WAITING_TRANSPORT,
48                 SENDING_REQUEST,
49                 WAITING_RESPONSE,
50                 RECEIVING_RESPONSE,
51                 ABORTED_BY_TRANSPORT,
52                 COMPLETED
53         } status;
54         enum {
55                 SEND_REQUEST_LINE,
56                 SEND_HEADER_MINE,
57                 SEND_HEADER_USER,
58                 SEND_MESSAGE_BODY,
59         } snd_state;
60         enum {
61                 RECEIVE_STATUS_LINE,
62                 RECEIVE_HEADER,
63                 RECEIVE_HEADER_END,
64                 RECEIVE_MESSAGE_BODY,
65                 RECEIVE_MESSAGE_END,
66         } rcv_state;
67         Bool aborted_by_user;
68         UB *host;
69         W host_len;
70         SOCKADDR addr;
71         ID id;
72 };
73 typedef struct http_reqentry_t_ http_reqentry_t;
74
75 LOCAL VOID http_reqentry_attachendpoint(http_reqentry_t *entry)
76 {
77         entry->status = SENDING_REQUEST;
78         /* TODO */
79         return;
80 }
81
82 LOCAL VOID http_reqentry_detachendpoint(http_reqentry_t *entry)
83 {
84         entry->status = WAITING_TRANSPORT;
85         /* TODO */
86         return;
87 }
88
89 LOCAL W http_reqentry_allocate(http_reqentry_t *entry, UB *host, W host_len, UH port, ID id)
90 {
91         W err;
92         B buf[HBUFLEN];
93         HOSTENT ent;
94         struct sockaddr_in *addr_in;
95
96         entry->host = malloc(sizeof(UB)*(host_len+1));
97         if (entry->host == NULL) {
98                 err = ER_NOMEM; /* TODO: detail error code */
99                 goto error_host;
100         }
101         memcpy(entry->host, host, host_len);
102         entry->host[host_len] = '\0';
103         entry->host_len = host_len;
104
105         err = so_gethostbyname(entry->host, &ent, buf);
106         if (err < 0) {
107                 goto error_gethostbyname;
108         }
109         addr_in = (struct sockaddr_in *)&(entry->addr);
110         addr_in->sin_family = AF_INET;
111         addr_in->sin_port = htons( port );
112         addr_in->sin_addr.s_addr = *(unsigned int *)(ent.h_addr_list[0]);
113
114         entry->aborted_by_user = False;
115         entry->status = WAITING_TRANSPORT;
116         entry->snd_state = SEND_REQUEST_LINE;
117         entry->rcv_state = RECEIVE_STATUS_LINE;
118         entry->id = id;
119
120         return 0;
121
122 error_gethostbyname:
123         free(entry->host);
124 error_host:
125         entry->status = NON_EXISTENT;
126         return err;
127 }
128
129 LOCAL VOID http_reqentry_free(http_reqentry_t *entry)
130 {
131         entry->id = -1;
132         entry->host_len = 0;
133         if (entry->host != NULL) {
134                 free(entry->host);
135         }
136         entry->status = NON_EXISTENT;
137 }
138
139 LOCAL VOID http_reqentry_initialize(http_reqentry_t *entry)
140 {
141         entry->status = NON_EXISTENT;
142         entry->host = NULL;
143         entry->host_len = 0;
144         entry->id = -1;
145 }
146
147 LOCAL VOID http_reqentry_finalize(http_reqentry_t *entry)
148 {
149         if (entry->host != NULL) {
150                 free(entry->host);
151         }
152 }
153
154 struct http_reqdict_t_ {
155         W entries;
156         http_reqentry_t entry[1];
157 };
158 typedef struct http_reqdict_t_ http_reqdict_t;
159
160 LOCAL ID http_reqdict_allocate(http_reqdict_t *dict, UB *host, W host_len, UH port)
161 {
162         W i, err;
163
164         for (i = 0; i < dict->entries; i++) {
165                 if (dict->entry[i].status == NON_EXISTENT) {
166                         break;
167                 }
168         }
169         if (i == dict->entries) {
170                 return ER_NOMEM; /* TODO: detail error code */
171         }
172
173         err = http_reqentry_allocate(dict->entry + i, host, host_len, port, i);
174         if (err < 0) {
175                 return err;
176         }
177
178         return i;
179 }
180
181 LOCAL http_reqentry_t* http_reqdict_getentrybyID(http_reqdict_t *dict, ID id)
182 {
183         http_reqentry_t *entry;
184         entry = dict->entry + id;
185         if (entry->status == NON_EXISTENT) {
186                 return NULL;
187         }
188         return entry;
189 }
190
191 LOCAL VOID http_reqdict_free(http_reqdict_t *dict, ID id)
192 {
193         http_reqentry_t *entry;
194         entry = http_reqdict_getentrybyID(dict, id);
195         if (entry == NULL) {
196                 return;
197         }
198         http_reqentry_free(entry);
199 }
200
201 LOCAL http_reqdict_t* http_reqdict_new(W max_entries)
202 {
203         http_reqdict_t *dict;
204         W i;
205
206         dict = (http_reqdict_t*)malloc(sizeof(W)*sizeof(http_reqdict_t)*max_entries);
207         if (dict == NULL) {
208                 return NULL;
209         }
210         dict->entries = max_entries;
211         for (i = 0; i < max_entries; i++) {
212                 http_reqentry_initialize(dict->entry+i);
213         }
214         return dict;
215 }
216
217 LOCAL VOID http_reqdict_delete(http_reqdict_t *dict)
218 {
219         W i;
220         for (i = 0; i < dict->entries; i++) {
221                 http_reqentry_finalize(dict->entry+i);
222         }
223         free(dict);
224 }
225
226 typedef struct {
227         http_reqdict_t *dict;
228         W i;
229 } http_recdictiterator_t;
230
231 LOCAL Bool http_reqdictiterator_next(http_recdictiterator_t *iter, http_reqentry_t **entry)
232 {
233         http_reqentry_t *e0;
234
235         for (; iter->i < iter->dict->entries;) {
236                 e0 = iter->dict->entry + iter->i;
237                 iter->i++;
238                 if (e0->status != NON_EXISTENT) {
239                         *entry = e0;
240                         return True;
241                 }
242         }
243
244         return False;
245 }
246
247 LOCAL VOID http_reqdictiterator_initialize(http_recdictiterator_t *iter, http_reqdict_t *dict)
248 {
249         iter->dict = dict;
250         iter->i = 0;
251 }
252
253 LOCAL VOID http_reqdictiterator_finalize(http_recdictiterator_t *iter)
254 {
255 }
256
257 struct http_connector_t_ {
258         http_reqdict_t *dict;
259         ID reqmbf;
260         ID evtmbf;
261 };
262
263 EXPORT ID http_connector_createendpoint(http_connector_t *connector, UB *host, W host_len, UH port)
264 {
265         ID id;
266         W err;
267
268         id = http_reqdict_allocate(connector->dict, host, host_len, port);
269         if (id < 0) {
270                 return id; /* TODO */
271         }
272
273         err = snd_mbf(connector->reqmbf, NULL, 0, T_FOREVER/* tmp */);
274         if (err < 0) {
275                 http_reqdict_free(connector->dict, id);
276                 return err;
277         }
278
279         return id;
280 }
281
282 EXPORT VOID http_connector_deleteendpoint(http_connector_t *connector, ID endpoint)
283 {
284         http_reqentry_t *entry;
285
286         entry = http_reqdict_getentrybyID(connector->dict, endpoint);
287         if (entry == NULL) {
288                 return;
289         }
290         entry->aborted_by_user = True;
291 }
292
293 LOCAL VOID http_connector_collect(http_connector_t *connector)
294 {
295         http_reqentry_t *entry;
296         http_recdictiterator_t iter;
297         Bool cont;
298
299         http_reqdictiterator_initialize(&iter, connector->dict);
300         for (;;) {
301                 cont = http_reqdictiterator_next(&iter, &entry);
302                 if (cont == False) {
303                         break;
304                 }
305                 if (entry->status == ABORTED_BY_TRANSPORT) {
306                         http_reqdict_free(connector->dict, entry->id);
307                 } else if (entry->status == COMPLETED) {
308                         /* TODO: release transport endpoint */
309                         http_reqdict_free(connector->dict, entry->id);
310                 }
311         }
312         http_reqdictiterator_finalize(&iter);
313 }
314
315 LOCAL W http_connector_searchwaiting(http_connector_t *connector)
316 {
317         W ret = 0;
318         http_reqentry_t *entry;
319         http_recdictiterator_t iter;
320         Bool cont;
321
322         http_reqdictiterator_initialize(&iter, connector->dict);
323         for (;;) {
324                 cont = http_reqdictiterator_next(&iter, &entry);
325                 if (cont == False) {
326                         break;
327                 }
328                 if (entry->status == WAITING_TRANSPORT) {
329                         /* TODO: transport end point search */
330                         /* TODO: transport end point status change: HOLDED and sending */
331                         http_reqentry_attachendpoint(entry);
332                         ret++;
333                 }
334         }
335         http_reqdictiterator_finalize(&iter);
336
337         return ret;
338 }
339
340 LOCAL W http_connector_waitreceiving(http_connector_t *connector)
341 {
342         W ret = 0;
343         http_reqentry_t *entry;
344         http_recdictiterator_t iter;
345         Bool cont;
346
347         http_reqdictiterator_initialize(&iter, connector->dict);
348         for (;;) {
349                 cont = http_reqdictiterator_next(&iter, &entry);
350                 if (cont == False) {
351                         break;
352                 }
353                 if (entry->status == WAITING_RESPONSE) {
354                         /* TODO: end point status change: readinging */
355                         /* TODO: select() for end point */
356                         ret++;
357                 }
358         }
359         http_reqdictiterator_finalize(&iter);
360
361         return ret;
362 }
363
364 EXPORT W http_connector_waitconnection(http_connector_t *connector, TMOUT tmout)
365 {
366         W err;
367         Bool evt = False;
368
369         /**/
370         http_connector_collect(connector);
371
372         err = http_connector_searchwaiting(connector);
373         if (err < 0) {
374                 return err;
375         }
376         if (err > 0) {
377                 evt = True;
378         }
379
380         err = http_connector_waitreceiving(connector);
381         if (err < 0) {
382                 return err;
383         }
384         if (err > 0) {
385                 evt = True;
386         }
387
388         if (evt != False) {
389                 snd_mbf(connector->evtmbf, NULL, 0, T_FOREVER);
390         }
391
392         return 0;
393 }
394
395 EXPORT Bool http_connector_searcheventtarget(http_connector_t *connector, http_connector_event *event)
396 {
397         http_reqentry_t *entry;
398         http_recdictiterator_t iter;
399         Bool cont, found = False;
400
401         http_reqdictiterator_initialize(&iter, connector->dict);
402         for (;;) {
403                 cont = http_reqdictiterator_next(&iter, &entry);
404                 if (cont == False) {
405                         break;
406                 }
407                 if (entry->status == SENDING_REQUEST) {
408                         event->type = HTTP_CONNECTOR_EVENTTYPE_SEND;
409                         event->endpoint = entry->id;
410                         found = True;
411                         break;
412                 }
413                 if (entry->status == RECEIVING_RESPONSE) {
414                         event->type = HTTP_CONNECTOR_EVENTTYPE_RECEIVE_MESSAGEBODY_END;
415                         event->endpoint = entry->id;
416                         found = True;
417                         break;
418                 }
419         }
420         http_reqdictiterator_finalize(&iter);
421
422         return found;
423 }
424
425 EXPORT W http_connector_getevent(http_connector_t *connector, http_connector_event *event)
426 {
427         W err;
428         Bool found;
429
430         err = rcv_mbf(connector->evtmbf, event, T_NOWAIT);
431         if ((err & 0xFF) == ER_NONE) {
432                 found = http_connector_searcheventtarget(connector, event);
433                 if (found != False) {
434                         return 0;
435                 }
436                 return err;
437         }
438         return err;
439 }
440
441 #define HTTP_CONNECTOR_SENDXXX_GET_CHECK(connector, endpoint, state, entry) \
442         (entry) = http_reqdict_getentrybyID((connector)->dict, (endpoint)); \
443         if ((entry) == NULL) { \
444                 return ER_NOEXS; /* TODO: detail error code */ \
445         } \
446         if ((entry)->status != SENDING_REQUEST) { \
447                 return ER_PAR; /* TODO: suitable error code */ \
448         } \
449         if ((entry)->snd_state != (state)) { \
450                 return ER_PAR; /* TODO: suitable error code */ \
451         } \
452
453 #define HTTP_CONNECTOR_SENDXXX_GET_CHECK_2(connector, endpoint, state1, state2, entry) \
454         (entry) = http_reqdict_getentrybyID((connector)->dict, (endpoint)); \
455         if ((entry) == NULL) { \
456                 return ER_NOEXS; /* TODO: detail error code */ \
457         } \
458         if ((entry)->status != SENDING_REQUEST) { \
459                 return ER_PAR; /* TODO: suitable error code */ \
460         } \
461         if (((entry)->snd_state != (state1))&&((entry)->snd_state != (state2))) { \
462                 return ER_PAR; /* TODO: suitable error code */ \
463         } \
464
465 EXPORT W http_connector_sendrequestline(http_connector_t *connector, ID endpoint, UB *path, W path_len)
466 {
467         W err;
468         http_reqentry_t *entry;
469
470         HTTP_CONNECTOR_SENDXXX_GET_CHECK(connector, endpoint, SEND_REQUEST_LINE, entry);
471
472         entry->snd_state = SEND_HEADER_MINE;
473
474         return 0;
475 }
476
477 EXPORT W http_connector_sendheader(http_connector_t *connector, ID endpoint, UB *p, W len)
478 {
479         W err;
480         http_reqentry_t *entry;
481
482         HTTP_CONNECTOR_SENDXXX_GET_CHECK_2(connector, endpoint, SEND_HEADER_MINE, SEND_HEADER_USER, entry);
483
484         if (entry->snd_state == SEND_HEADER_MINE) {
485                 entry->snd_state = SEND_HEADER_USER;
486         }
487
488         return 0;
489 }
490
491 EXPORT W http_connector_sendheaderend(http_connector_t *connector, ID endpoint)
492 {
493         W err;
494         http_reqentry_t *entry;
495
496         HTTP_CONNECTOR_SENDXXX_GET_CHECK_2(connector, endpoint, SEND_HEADER_MINE, SEND_HEADER_USER, entry);
497
498         if (entry->snd_state == SEND_HEADER_MINE) {
499         }
500
501         entry->snd_state = SEND_MESSAGE_BODY;
502
503         return 0;
504 }
505
506 EXPORT W http_connector_sendmessagebody(http_connector_t *connector, ID endpoint, UB *p, W len)
507 {
508         W err;
509         http_reqentry_t *entry;
510
511         HTTP_CONNECTOR_SENDXXX_GET_CHECK(connector, endpoint, SEND_MESSAGE_BODY, entry);
512
513         return 0;
514 }
515
516 EXPORT W http_connector_sendmessagebodyend(http_connector_t *connector, ID endpoint, UB *p, W len)
517 {
518         http_reqentry_t *entry;
519
520         HTTP_CONNECTOR_SENDXXX_GET_CHECK(connector, endpoint, SEND_REQUEST_LINE, entry);        
521
522         entry->status = COMPLETED;
523
524         return 0;
525 }
526
527 EXPORT http_connector_t* http_connector_new()
528 {
529         http_connector_t *connector;
530
531         connector = (http_connector_t*)malloc(sizeof(http_connector_t));
532         if (connector == NULL) {
533                 DP_ER("malloc", 0);
534                 return NULL;
535         }
536         connector->dict = http_reqdict_new(10/*tmp*/);
537         if (connector->dict == NULL) {
538                 DP_ER("http_recdict_new", 0);
539                 goto error_http_reqdict;
540         }
541         connector->reqmbf = cre_mbf(0, 1, DELEXIT);
542         if (connector->reqmbf < 0) {
543                 DP_ER("cre_mbf: reqmbf", connector->reqmbf);
544                 goto error_reqmbf;
545         }
546         connector->evtmbf = cre_mbf(0, 1, DELEXIT);
547         if (connector->evtmbf < 0) {
548                 DP_ER("cre_mbf: evtmbf", connector->evtmbf);
549                 goto error_evtmbf;
550         }
551
552         return connector;
553
554         del_mbf(connector->evtmbf);
555 error_evtmbf:
556         del_mbf(connector->reqmbf);
557 error_reqmbf:
558         http_reqdict_delete(connector->dict);
559 error_http_reqdict:
560         free(connector);
561         return NULL;
562 }
563
564 EXPORT VOID http_connector_delete(http_connector_t *connector)
565 {
566         del_mbf(connector->evtmbf);
567         del_mbf(connector->reqmbf);
568         http_reqdict_delete(connector->dict);
569         free(connector);
570 }