4 * Copyright (c) 2012 project bchan
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.
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:
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.
19 * 2. Altered source versions must be plainly marked as such, and must not be
20 * misrepresented as being the original software.
22 * 3. This notice may not be removed or altered from any source
27 #include "http_connector.h"
33 #include <btron/btron.h>
34 #include <btron/bsocket.h>
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)
41 # define DP_ER(msg, err) /**/
44 struct http_reqentry_t_ {
73 typedef struct http_reqentry_t_ http_reqentry_t;
75 LOCAL VOID http_reqentry_attachendpoint(http_reqentry_t *entry)
77 entry->status = SENDING_REQUEST;
82 LOCAL VOID http_reqentry_detachendpoint(http_reqentry_t *entry)
84 entry->status = WAITING_TRANSPORT;
89 LOCAL W http_reqentry_allocate(http_reqentry_t *entry, UB *host, W host_len, UH port, ID id)
94 struct sockaddr_in *addr_in;
96 entry->host = malloc(sizeof(UB)*(host_len+1));
97 if (entry->host == NULL) {
98 err = ER_NOMEM; /* TODO: detail error code */
101 memcpy(entry->host, host, host_len);
102 entry->host[host_len] = '\0';
103 entry->host_len = host_len;
105 err = so_gethostbyname(entry->host, &ent, buf);
107 goto error_gethostbyname;
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]);
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;
125 entry->status = NON_EXISTENT;
129 LOCAL VOID http_reqentry_free(http_reqentry_t *entry)
133 if (entry->host != NULL) {
136 entry->status = NON_EXISTENT;
139 LOCAL VOID http_reqentry_initialize(http_reqentry_t *entry)
141 entry->status = NON_EXISTENT;
147 LOCAL VOID http_reqentry_finalize(http_reqentry_t *entry)
149 if (entry->host != NULL) {
154 struct http_reqdict_t_ {
156 http_reqentry_t entry[1];
158 typedef struct http_reqdict_t_ http_reqdict_t;
160 LOCAL ID http_reqdict_allocate(http_reqdict_t *dict, UB *host, W host_len, UH port)
164 for (i = 0; i < dict->entries; i++) {
165 if (dict->entry[i].status == NON_EXISTENT) {
169 if (i == dict->entries) {
170 return ER_NOMEM; /* TODO: detail error code */
173 err = http_reqentry_allocate(dict->entry + i, host, host_len, port, i);
181 LOCAL http_reqentry_t* http_reqdict_getentrybyID(http_reqdict_t *dict, ID id)
183 http_reqentry_t *entry;
184 entry = dict->entry + id;
185 if (entry->status == NON_EXISTENT) {
191 LOCAL VOID http_reqdict_free(http_reqdict_t *dict, ID id)
193 http_reqentry_t *entry;
194 entry = http_reqdict_getentrybyID(dict, id);
198 http_reqentry_free(entry);
201 LOCAL http_reqdict_t* http_reqdict_new(W max_entries)
203 http_reqdict_t *dict;
206 dict = (http_reqdict_t*)malloc(sizeof(W)*sizeof(http_reqdict_t)*max_entries);
210 dict->entries = max_entries;
211 for (i = 0; i < max_entries; i++) {
212 http_reqentry_initialize(dict->entry+i);
217 LOCAL VOID http_reqdict_delete(http_reqdict_t *dict)
220 for (i = 0; i < dict->entries; i++) {
221 http_reqentry_finalize(dict->entry+i);
227 http_reqdict_t *dict;
229 } http_recdictiterator_t;
231 LOCAL Bool http_reqdictiterator_next(http_recdictiterator_t *iter, http_reqentry_t **entry)
235 for (; iter->i < iter->dict->entries;) {
236 e0 = iter->dict->entry + iter->i;
238 if (e0->status != NON_EXISTENT) {
247 LOCAL VOID http_reqdictiterator_initialize(http_recdictiterator_t *iter, http_reqdict_t *dict)
253 LOCAL VOID http_reqdictiterator_finalize(http_recdictiterator_t *iter)
257 struct http_connector_t_ {
258 http_reqdict_t *dict;
263 EXPORT ID http_connector_createendpoint(http_connector_t *connector, UB *host, W host_len, UH port)
268 id = http_reqdict_allocate(connector->dict, host, host_len, port);
270 return id; /* TODO */
273 err = snd_mbf(connector->reqmbf, NULL, 0, T_FOREVER/* tmp */);
275 http_reqdict_free(connector->dict, id);
282 EXPORT VOID http_connector_deleteendpoint(http_connector_t *connector, ID endpoint)
284 http_reqentry_t *entry;
286 entry = http_reqdict_getentrybyID(connector->dict, endpoint);
290 entry->aborted_by_user = True;
293 LOCAL VOID http_connector_collect(http_connector_t *connector)
295 http_reqentry_t *entry;
296 http_recdictiterator_t iter;
299 http_reqdictiterator_initialize(&iter, connector->dict);
301 cont = http_reqdictiterator_next(&iter, &entry);
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);
312 http_reqdictiterator_finalize(&iter);
315 LOCAL W http_connector_searchwaiting(http_connector_t *connector)
318 http_reqentry_t *entry;
319 http_recdictiterator_t iter;
322 http_reqdictiterator_initialize(&iter, connector->dict);
324 cont = http_reqdictiterator_next(&iter, &entry);
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);
335 http_reqdictiterator_finalize(&iter);
340 LOCAL W http_connector_waitreceiving(http_connector_t *connector)
343 http_reqentry_t *entry;
344 http_recdictiterator_t iter;
347 http_reqdictiterator_initialize(&iter, connector->dict);
349 cont = http_reqdictiterator_next(&iter, &entry);
353 if (entry->status == WAITING_RESPONSE) {
354 /* TODO: end point status change: readinging */
355 /* TODO: select() for end point */
359 http_reqdictiterator_finalize(&iter);
364 EXPORT W http_connector_waitconnection(http_connector_t *connector, TMOUT tmout)
370 http_connector_collect(connector);
372 err = http_connector_searchwaiting(connector);
380 err = http_connector_waitreceiving(connector);
389 snd_mbf(connector->evtmbf, NULL, 0, T_FOREVER);
395 EXPORT Bool http_connector_searcheventtarget(http_connector_t *connector, http_connector_event *event)
397 http_reqentry_t *entry;
398 http_recdictiterator_t iter;
399 Bool cont, found = False;
401 http_reqdictiterator_initialize(&iter, connector->dict);
403 cont = http_reqdictiterator_next(&iter, &entry);
407 if (entry->status == SENDING_REQUEST) {
408 event->type = HTTP_CONNECTOR_EVENTTYPE_SEND;
409 event->endpoint = entry->id;
413 if (entry->status == RECEIVING_RESPONSE) {
414 event->type = HTTP_CONNECTOR_EVENTTYPE_RECEIVE_MESSAGEBODY_END;
415 event->endpoint = entry->id;
420 http_reqdictiterator_finalize(&iter);
425 EXPORT W http_connector_getevent(http_connector_t *connector, http_connector_event *event)
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) {
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 */ \
446 if ((entry)->status != SENDING_REQUEST) { \
447 return ER_PAR; /* TODO: suitable error code */ \
449 if ((entry)->snd_state != (state)) { \
450 return ER_PAR; /* TODO: suitable error code */ \
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 */ \
458 if ((entry)->status != SENDING_REQUEST) { \
459 return ER_PAR; /* TODO: suitable error code */ \
461 if (((entry)->snd_state != (state1))&&((entry)->snd_state != (state2))) { \
462 return ER_PAR; /* TODO: suitable error code */ \
465 EXPORT W http_connector_sendrequestline(http_connector_t *connector, ID endpoint, UB *path, W path_len)
468 http_reqentry_t *entry;
470 HTTP_CONNECTOR_SENDXXX_GET_CHECK(connector, endpoint, SEND_REQUEST_LINE, entry);
472 entry->snd_state = SEND_HEADER_MINE;
477 EXPORT W http_connector_sendheader(http_connector_t *connector, ID endpoint, UB *p, W len)
480 http_reqentry_t *entry;
482 HTTP_CONNECTOR_SENDXXX_GET_CHECK_2(connector, endpoint, SEND_HEADER_MINE, SEND_HEADER_USER, entry);
484 if (entry->snd_state == SEND_HEADER_MINE) {
485 entry->snd_state = SEND_HEADER_USER;
491 EXPORT W http_connector_sendheaderend(http_connector_t *connector, ID endpoint)
494 http_reqentry_t *entry;
496 HTTP_CONNECTOR_SENDXXX_GET_CHECK_2(connector, endpoint, SEND_HEADER_MINE, SEND_HEADER_USER, entry);
498 if (entry->snd_state == SEND_HEADER_MINE) {
501 entry->snd_state = SEND_MESSAGE_BODY;
506 EXPORT W http_connector_sendmessagebody(http_connector_t *connector, ID endpoint, UB *p, W len)
509 http_reqentry_t *entry;
511 HTTP_CONNECTOR_SENDXXX_GET_CHECK(connector, endpoint, SEND_MESSAGE_BODY, entry);
516 EXPORT W http_connector_sendmessagebodyend(http_connector_t *connector, ID endpoint, UB *p, W len)
518 http_reqentry_t *entry;
520 HTTP_CONNECTOR_SENDXXX_GET_CHECK(connector, endpoint, SEND_REQUEST_LINE, entry);
522 entry->status = COMPLETED;
527 EXPORT http_connector_t* http_connector_new()
529 http_connector_t *connector;
531 connector = (http_connector_t*)malloc(sizeof(http_connector_t));
532 if (connector == NULL) {
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;
541 connector->reqmbf = cre_mbf(0, 1, DELEXIT);
542 if (connector->reqmbf < 0) {
543 DP_ER("cre_mbf: reqmbf", connector->reqmbf);
546 connector->evtmbf = cre_mbf(0, 1, DELEXIT);
547 if (connector->evtmbf < 0) {
548 DP_ER("cre_mbf: evtmbf", connector->evtmbf);
554 del_mbf(connector->evtmbf);
556 del_mbf(connector->reqmbf);
558 http_reqdict_delete(connector->dict);
564 EXPORT VOID http_connector_delete(http_connector_t *connector)
566 del_mbf(connector->evtmbf);
567 del_mbf(connector->reqmbf);
568 http_reqdict_delete(connector->dict);