test_transport.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. #ifndef _TEST_HTTP_TRANSPORT_H_
  2. #define _TEST_HTTP_TRANSPORT_H_
  3. #define TEST_HTTP_CAT_(A, B) A ## B
  4. #define TEST_HTTP_CAT(A, B) TEST_HTTP_CAT_(A, B)
  5. #define TEST_HTTP_STRING_(A) #A
  6. #define TEST_HTTP_STRING(A) TEST_HTTP_STRING_(A)
  7. #define TEST_HTTP_LOCAL_IP "0.0.0.0"
  8. #define TEST_HTTP_LOCAL_PORT 8080
  9. #define TEST_HTTP_REMOTE_IP "google.com"
  10. #define TEST_HTTP_REMOTE_PORT 80
  11. #define TEST_HTTP_MIN_STREAM_CHUNCK_SIZE 0x32
  12. #define TEST_HTTP_GET "GET / HTTP/1.1\r\n" \
  13. "Host: " TEST_HTTP_REMOTE_IP ":" TEST_HTTP_STRING(TEST_HTTP_REMOTE_PORT) "\r\n" \
  14. "Connection: keep-alive\r\n" \
  15. "Cache-Control: max-age=0\r\n" \
  16. "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" \
  17. "User-Agent: Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.36 (KHTML, like Gecko)\r\n" \
  18. "Doubango 2.0\r\n" \
  19. "Accept-Encoding: gzip,deflate,sdch\r\n" \
  20. "Accept-Language: en-US,en;q=0.8\r\n" \
  21. "\r\n" \
  22. static int test_http_transport_callback(const tnet_transport_event_t* e);
  23. /************************************************
  24. * test_http_peer_t
  25. ************************************************/
  26. typedef struct test_http_peer_s {
  27. TSK_DECLARE_OBJECT;
  28. tnet_fd_t fd;
  29. tsk_buffer_t* buff;
  30. }
  31. test_http_peer_t;
  32. typedef tsk_list_t test_http_peers_L_t;
  33. static tsk_object_t* test_http_peer_ctor(tsk_object_t * self, va_list * app)
  34. {
  35. return self;
  36. }
  37. static tsk_object_t* test_http_peer_dtor(tsk_object_t * self)
  38. {
  39. test_http_peer_t *peer = self;
  40. if (peer) {
  41. TSK_OBJECT_SAFE_FREE(peer->buff);
  42. }
  43. return self;
  44. }
  45. static const tsk_object_def_t test_http_peer_def_s = {
  46. sizeof(test_http_peer_t),
  47. test_http_peer_ctor,
  48. test_http_peer_dtor,
  49. tsk_null,
  50. };
  51. static test_http_peer_t* test_http_peer_create(tnet_fd_t fd)
  52. {
  53. test_http_peer_t* peer = tsk_object_new(&test_http_peer_def_s);
  54. peer->fd = fd;
  55. peer->buff = tsk_buffer_create_null();
  56. return peer;
  57. }
  58. static int test_http_peer_pred_fd_cmp(const tsk_list_item_t* item, const void* data)
  59. {
  60. return ((test_http_peer_t*)item->data)->fd - *((const tnet_fd_t*)data);
  61. }
  62. /************************************************
  63. * test_http_transport_t
  64. ************************************************/
  65. typedef struct test_http_transport_s {
  66. TSK_DECLARE_OBJECT;
  67. test_http_peers_L_t* peers;
  68. tnet_transport_handle_t* handle;
  69. }
  70. test_http_transport_t;
  71. static tsk_object_t* test_http_transport_ctor(tsk_object_t * self, va_list * app)
  72. {
  73. test_http_transport_t *transport = self;
  74. if (transport) {
  75. }
  76. return self;
  77. }
  78. static tsk_object_t* test_http_transport_dtor(tsk_object_t * self)
  79. {
  80. test_http_transport_t *transport = self;
  81. if(transport) {
  82. TSK_OBJECT_SAFE_FREE(transport->handle);
  83. TSK_OBJECT_SAFE_FREE(transport->peers);
  84. }
  85. return self;
  86. }
  87. static const tsk_object_def_t test_http_transport_def_s = {
  88. sizeof(test_http_transport_t),
  89. test_http_transport_ctor,
  90. test_http_transport_dtor,
  91. tsk_null,
  92. };
  93. static test_http_transport_t* test_http_transport_create(const char* local_ip, tnet_port_t local_port, enum tnet_socket_type_e socket_type)
  94. {
  95. test_http_transport_t *transport = tsk_object_new(&test_http_transport_def_s);
  96. if (transport) {
  97. transport->peers = tsk_list_create();
  98. transport->handle = tnet_transport_create(local_ip, local_port, socket_type, "HTTP Transport");
  99. if (!transport->handle) {
  100. TSK_OBJECT_SAFE_FREE(transport);
  101. return tsk_null;
  102. }
  103. tnet_transport_set_callback(transport->handle, test_http_transport_callback, transport);
  104. }
  105. return transport;
  106. }
  107. static int test_http_transport_start(test_http_transport_t* self)
  108. {
  109. if (self) {
  110. return tnet_transport_start(self->handle);
  111. }
  112. return -1;
  113. }
  114. // Up to the caller to free the returned object using TSK_OBJECT_SAFREE(peer)
  115. static test_http_peer_t* test_http_transport_connect_to(test_http_transport_t* self, const char* dst_host, tnet_port_t dst_port)
  116. {
  117. tnet_fd_t fd = tnet_transport_connectto_2(self->handle, dst_host, dst_port);
  118. if (fd > 0) {
  119. return test_http_peer_create(fd);
  120. }
  121. return tsk_null;
  122. }
  123. static tsk_size_t test_http_transport_send_data(test_http_transport_t* self, tnet_fd_t fd, const void* data_ptr, tsk_size_t data_size)
  124. {
  125. if (self && self->handle && data_ptr && data_size) {
  126. return tnet_transport_send(self->handle, fd, data_ptr, data_size);
  127. }
  128. return 0;
  129. }
  130. static void test_http_transport_add_peer(test_http_transport_t* self, test_http_peer_t* peer)
  131. {
  132. tsk_list_lock(self->peers);
  133. tsk_list_push_back_data(self->peers, &peer);
  134. tsk_list_unlock(self->peers);
  135. }
  136. static void test_http_transport_remove_peer_by_fd(test_http_transport_t* self, tnet_fd_t fd)
  137. {
  138. tsk_list_lock(self->peers);
  139. tsk_list_remove_item_by_pred(self->peers, test_http_peer_pred_fd_cmp, &fd);
  140. tsk_list_unlock(self->peers);
  141. }
  142. static const test_http_peer_t* test_http_transport_find_peer_by_fd(test_http_transport_t* self, tnet_fd_t fd)
  143. {
  144. const tsk_list_item_t* item;
  145. tsk_list_lock(self->peers);
  146. item = tsk_list_find_item_by_pred(self->peers, test_http_peer_pred_fd_cmp, &fd);
  147. tsk_list_unlock(self->peers);
  148. if (item) {
  149. return (const test_http_peer_t*)item->data;
  150. }
  151. return tsk_null;
  152. }
  153. static void test_http_transport_process_incoming_msg(test_http_transport_t* self, const thttp_message_t* msg, tnet_fd_t fd)
  154. {
  155. if (THTTP_MESSAGE_IS_REQUEST(msg)) {
  156. if (tsk_striequals(msg->line.request.method, "GET")) {
  157. char* result = tsk_null;
  158. const char* content = "<html><body>Hello world!</body></html>";
  159. int len = tsk_sprintf(
  160. (char**)&result,
  161. "HTTP/1.1 %u %s\r\n"
  162. "Server: My test server \r\n"
  163. "Access-Control-Allow-Origin: *\r\n"
  164. "Content-Length: %u\r\n"
  165. "Content-Type: text/html\r\n"
  166. "Connection: Close\r\n"
  167. "\r\n"
  168. "%s", 200, "OK", tsk_strlen(content), content);
  169. tnet_transport_send(self, fd, result, len);
  170. TSK_FREE(result);
  171. }
  172. }
  173. }
  174. static int test_http_transport_callback(const tnet_transport_event_t* e)
  175. {
  176. test_http_transport_t* transport = (test_http_transport_t*)e->callback_data;
  177. const test_http_peer_t* _peer;
  178. thttp_message_t *message = tsk_null;
  179. int endOfheaders = -1;
  180. tsk_ragel_state_t state;
  181. tsk_bool_t have_all_content = tsk_false;
  182. int ret;
  183. switch (e->type) {
  184. case event_closed: {
  185. test_http_transport_remove_peer_by_fd(transport, e->local_fd);
  186. return 0;
  187. }
  188. case event_connected:
  189. case event_accepted: {
  190. _peer = test_http_transport_find_peer_by_fd(transport, e->local_fd);
  191. if (!_peer) {
  192. _peer = test_http_peer_create(e->local_fd);
  193. test_http_transport_add_peer(transport, (test_http_peer_t*)_peer);
  194. }
  195. return 0;
  196. }
  197. case event_data: {
  198. TSK_DEBUG_INFO("\n\nRECV: %.*s\n\n", e->size, (const char*)e->data);
  199. break;
  200. }
  201. default:
  202. return 0;
  203. }
  204. _peer = test_http_transport_find_peer_by_fd(transport, e->local_fd);
  205. if(!_peer) {
  206. TSK_DEBUG_ERROR("Data event but no peer found!");
  207. return -1;
  208. }
  209. /* Append new content. */
  210. tsk_buffer_append(_peer->buff, e->data, e->size);
  211. /* Check if we have all HTTP headers. */
  212. parse_buffer:
  213. if ((endOfheaders = tsk_strindexOf(TSK_BUFFER_DATA(_peer->buff), TSK_BUFFER_SIZE(_peer->buff), "\r\n\r\n"/*2CRLF*/)) < 0) {
  214. TSK_DEBUG_INFO("No all HTTP headers in the TCP buffer.");
  215. goto bail;
  216. }
  217. /* If we are here this mean that we have all HTTP headers.
  218. * ==> Parse the HTTP message without the content.
  219. */
  220. tsk_ragel_state_init(&state, TSK_BUFFER_DATA(_peer->buff), endOfheaders + 4/*2CRLF*/);
  221. if (!(ret = thttp_message_parse(&state, &message, tsk_false/* do not extract the content */))) {
  222. const thttp_header_Transfer_Encoding_t* transfer_Encoding;
  223. /* chunked? */
  224. if((transfer_Encoding = (const thttp_header_Transfer_Encoding_t*)thttp_message_get_header(message, thttp_htype_Transfer_Encoding)) && tsk_striequals(transfer_Encoding->encoding, "chunked")) {
  225. const char* start = (const char*)(TSK_BUFFER_TO_U8(_peer->buff) + (endOfheaders + 4/*2CRLF*/));
  226. const char* end = (const char*)(TSK_BUFFER_TO_U8(_peer->buff) + TSK_BUFFER_SIZE(_peer->buff));
  227. int index;
  228. TSK_DEBUG_INFO("CHUNKED transfer.");
  229. while(start < end) {
  230. /* RFC 2616 - 19.4.6 Introduction of Transfer-Encoding */
  231. // read chunk-size, chunk-extension (if any) and CRLF
  232. tsk_size_t chunk_size = (tsk_size_t)tsk_atox(start);
  233. if((index = tsk_strindexOf(start, (end-start), "\r\n")) >=0) {
  234. start += index + 2/*CRLF*/;
  235. }
  236. else {
  237. TSK_DEBUG_INFO("Parsing chunked data has failed.");
  238. break;
  239. }
  240. if(chunk_size == 0 && ((start + 2) <= end) && *start == '\r' && *(start+ 1) == '\n') {
  241. int parsed_len = (start - (const char*)(TSK_BUFFER_TO_U8(_peer->buff))) + 2/*CRLF*/;
  242. tsk_buffer_remove(_peer->buff, 0, parsed_len);
  243. have_all_content = tsk_true;
  244. break;
  245. }
  246. thttp_message_append_content(message, start, chunk_size);
  247. start += chunk_size + 2/*CRLF*/;
  248. }
  249. }
  250. else {
  251. tsk_size_t clen = THTTP_MESSAGE_CONTENT_LENGTH(message); /* MUST have content-length header. */
  252. if(clen == 0) { /* No content */
  253. tsk_buffer_remove(_peer->buff, 0, (endOfheaders + 4/*2CRLF*/)); /* Remove HTTP headers and CRLF ==> must never happen */
  254. have_all_content = tsk_true;
  255. }
  256. else { /* There is a content */
  257. if((endOfheaders + 4/*2CRLF*/ + clen) > TSK_BUFFER_SIZE(_peer->buff)) { /* There is content but not all the content. */
  258. TSK_DEBUG_INFO("No all HTTP content in the TCP buffer.");
  259. goto bail;
  260. }
  261. else {
  262. /* Add the content to the message. */
  263. thttp_message_add_content(message, tsk_null, TSK_BUFFER_TO_U8(_peer->buff) + endOfheaders + 4/*2CRLF*/, clen);
  264. /* Remove HTTP headers, CRLF and the content. */
  265. tsk_buffer_remove(_peer->buff, 0, (endOfheaders + 4/*2CRLF*/ + clen));
  266. have_all_content = tsk_true;
  267. }
  268. }
  269. }
  270. }
  271. /* Alert the dialog (FSM) */
  272. if(message) {
  273. if (have_all_content) { /* only if we have all data */
  274. test_http_transport_process_incoming_msg(transport, message, e->local_fd);
  275. /* Parse next chunck */
  276. if (TSK_BUFFER_SIZE(_peer->buff) >= TEST_HTTP_MIN_STREAM_CHUNCK_SIZE) {
  277. TSK_OBJECT_SAFE_FREE(message);
  278. goto parse_buffer;
  279. }
  280. }
  281. }
  282. bail:
  283. TSK_OBJECT_SAFE_FREE(message);
  284. return 0;
  285. }
  286. /************************************************
  287. * Main
  288. ************************************************/
  289. static void test_transport()
  290. {
  291. test_http_transport_t* p_transport = tsk_null;
  292. test_http_peer_t* peer = tsk_null;
  293. int ret;
  294. static const char* __get_msg = TEST_HTTP_GET;
  295. p_transport = test_http_transport_create(TEST_HTTP_LOCAL_IP, TEST_HTTP_LOCAL_PORT, tnet_socket_type_tcp_ipv4);
  296. if (!p_transport) {
  297. TSK_DEBUG_ERROR("Failed to HTTP transport");
  298. goto bail;
  299. }
  300. ret = test_http_transport_start(p_transport);
  301. if (ret) {
  302. TSK_DEBUG_ERROR("Failed to start HTTP transport with error code = %d", ret);
  303. goto bail;
  304. }
  305. getchar();
  306. // Send data to google.com
  307. peer = test_http_transport_connect_to(p_transport, TEST_HTTP_REMOTE_IP, TEST_HTTP_REMOTE_PORT);
  308. ret = tnet_sockfd_waitUntilWritable(peer->fd, 1000); // you should use the callback function instead of blocking the process
  309. if (ret) {
  310. TSK_DEBUG_ERROR("Failed to connect to(%s,%d) with error code = %d", TEST_HTTP_REMOTE_IP, TEST_HTTP_REMOTE_PORT, ret);
  311. goto bail;
  312. }
  313. ret = test_http_transport_send_data(p_transport, peer->fd, __get_msg, tsk_strlen(__get_msg));
  314. TSK_DEBUG_INFO("Sent %d bytes", ret);
  315. getchar();
  316. bail:
  317. TSK_OBJECT_SAFE_FREE(p_transport); // stop server and free memory
  318. TSK_OBJECT_SAFE_FREE(peer);
  319. }
  320. #endif /* _TEST_HTTP_TRANSPORT_H_ */