drive_wapbox.c
上传用户:gzpyjq
上传日期:2013-01-31
资源大小:1852k
文件大小:15k
源码类别:

手机WAP编程

开发平台:

WINDOWS

  1. /* drive_wapbox.c - test wapbox through its bearerbox and http interfaces
  2.  *
  3.  * This program starts a wapbox and pretends to be both the bearer box
  4.  * and the http server, so that it can test and benchmark the wapbox in
  5.  * isolation.
  6.  *
  7.  * Richard Braakman
  8.  */
  9. #include <string.h>
  10. #include <stdlib.h>
  11. #include <unistd.h>
  12. #include <errno.h>
  13. #include <signal.h>
  14. #include <sys/time.h>
  15. #include <sys/types.h>
  16. #include <sys/socket.h>
  17. #include "gwlib/gwlib.h"
  18. #include "gw/msg.h"
  19. /* These should really be in a header file */
  20. enum wsp_types {
  21.         Bad_PDU = -1,
  22.         Connect_PDU = 0x01,
  23.         ConnectReply_PDU = 0x02,
  24.         Redirect_PDU = 0x03,
  25.         Reply_PDU = 0x04,
  26.         Disconnect_PDU = 0x05,
  27.         Push_PDU = 0x06,
  28.         ConfirmedPush_PDU = 0x07,
  29.         Suspend_PDU = 0x08,
  30.         Resume_PDU = 0x09,
  31.         Get_PDU = 0x40,
  32.         Options_PDU = 0x41,
  33.         Head_PDU = 0x42,
  34.         Delete_PDU = 0x43,
  35.         Trace_PDU = 0x44,
  36.         Post_PDU = 0x60,
  37.         Put_PDU = 0x61
  38. };
  39. enum wtp_types {
  40. INVOKE = 1,
  41. RESULT = 2,
  42. ACK = 3
  43. };
  44. #define WSP_VERSION 0x10
  45. #define TIMEOUT 10.0  /* seconds */
  46. static long max_requests = 1;
  47. static long max_clients = 1;
  48. static long req_per_session = 1;
  49. static unsigned short http_port;
  50. static int wapbox_port = 30188;
  51. static Octstr *http_url = NULL;
  52. static int verbose_debug = 0;
  53. static int user_ack = 0;
  54. static long requests_complete = 0;
  55. static volatile sig_atomic_t dying = 0;
  56. enum WTP_type {
  57. TR_Invoke = 1,
  58. TR_Result = 2,
  59. TR_Ack = 3,
  60. TR_Abort = 4
  61. };
  62. struct client_status {
  63. /* True if we expect a WTP reply */
  64. int wtp_invoked;
  65. /* Transaction number for WTP level */
  66. int wtp_tid; /* current tid if wtp_invoked, else next tid to use */
  67. /* True if we're connected at the WSP level */
  68. /* Equal to 2 if we're trying to disconnect */
  69. int wsp_connected;
  70. /* -1 if we're not connected */
  71. long wsp_session_id;
  72. /* Number of successful page fetches this session */
  73. int pages_fetched;
  74. /* Source port to use for this client; should be unique. */
  75. unsigned short port;
  76. };
  77. typedef struct client_status Client;
  78. static Client *clients;
  79. List *ready_clients;
  80. static unsigned long get_varint(Octstr *pdu, int pos) {
  81. int c;
  82. long result = 0;
  83. do {
  84. c = octstr_get_char(pdu, pos++);
  85. result = (result << 7) | (c & 0x7f);
  86. } while (c & 0x80);
  87. return c;
  88. }
  89. static void http_thread(void *arg) {
  90. HTTPClient *client;
  91. Octstr *ip;
  92. Octstr *url;
  93. List *headers;
  94. Octstr *body;
  95. List *cgivars;
  96. Octstr *reply_body = octstr_create(
  97. "<?xml version="1.0"?>n"
  98. "<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"n"
  99. " "http://www.wapforum.org/DTD/wml_1.1.xml">n"
  100. "<wml>n"
  101. "<card id="main" title="Hello, world" newcontext="true">n"
  102. "        <p>Hello, world.</p>n"
  103. "</card></wml>n");
  104. List *reply_headers = list_create();
  105. int port;
  106.      port = *(int *) arg;
  107. gw_free(arg);
  108. list_append(reply_headers,
  109. octstr_create("Content-Type: text/vnd.wap.wml"));
  110. for (;!dying;) {
  111. client = http_accept_request(port, &ip, &url, &headers, 
  112.                     &body, &cgivars);
  113. if (client == NULL)
  114. break;
  115. http_send_reply(client, HTTP_OK, reply_headers, reply_body);
  116. http_destroy_headers(headers);
  117. octstr_destroy(ip);
  118. octstr_destroy(url);
  119. octstr_destroy(body);
  120. http_destroy_cgiargs(cgivars);
  121. }
  122. octstr_destroy(reply_body);
  123.      http_destroy_headers(reply_headers);
  124. }
  125. static long http_thread_id;
  126. static int start_http_thread(void) {
  127. unsigned short port;
  128. int *port_copy;
  129.     int ssl = 0;   /* indicate if SSL-enabled server should be used */
  130. for (port = 40000; port < 41000; port += 13) {
  131. if (http_open_port(port, ssl) != -1)
  132.     break;
  133. }
  134. if (port == 41000)
  135. panic(0, "No ports available for http server");
  136.      port_copy = gw_malloc(sizeof(*port_copy));
  137. *port_copy = port;
  138. http_thread_id = gwthread_create(http_thread, port_copy);
  139. if (http_thread_id == -1) 
  140. panic(0, "Cannot start http thread");
  141. return port;
  142. }
  143. static Connection *start_wapbox(void) {
  144. int wap_socket;
  145. int wapbox;
  146. wap_socket = make_server_socket(wapbox_port, NULL);
  147. if (wap_socket < 0)
  148. panic(0, "Couldn't make wapbox portn");
  149. wapbox = accept(wap_socket, NULL, NULL);
  150. if (wapbox < 0)
  151. panic(errno, "Wapbox could not connectn");
  152. close(wap_socket);
  153. return conn_wrap_fd(wapbox, 0);
  154. }
  155. static void initialize_clients(void) {
  156. long i;
  157. ready_clients = list_create();
  158. clients = gw_malloc(max_clients * sizeof(*clients));
  159. for (i = 0; i < max_clients; i++) {
  160. clients[i].wtp_invoked = 0;
  161. clients[i].wtp_tid = 0;
  162. clients[i].wsp_connected = 0;
  163. clients[i].wsp_session_id = -1;
  164. clients[i].pages_fetched = 0;
  165. clients[i].port = i;
  166. list_append(ready_clients, &clients[i]);
  167. }
  168. }
  169. static void destroy_clients(void) {
  170. gw_free(clients);
  171. list_destroy(ready_clients, NULL);
  172. }
  173. static Client *find_client(unsigned short port) {
  174. /* It's easy and fast since we assign ports in linear order */
  175. if (port >= max_clients)
  176. return NULL;
  177. return clients + port;
  178. }
  179. static void client_done(Client *client) {
  180. requests_complete++;
  181. list_append(ready_clients, client);
  182. }
  183. static void increment_tid(Client *client) {
  184. if (client->wtp_tid == 0x7fff) 
  185. client->wtp_tid = 0;
  186. else
  187. client->wtp_tid++;
  188. }
  189. /* Set the U/P flag on an Invoke PDU */
  190. static void set_user_ack(Octstr *pdu) {
  191. octstr_set_bits(pdu, 3 * 8 + 3, 1, 1);
  192. }
  193. static Octstr *wtp_invoke_create(int class) {
  194. Octstr *pdu;
  195. /* data describes a TR-Invoke PDU, with GTR=1 and TTR=1 (segmentation
  196.  * not supported), and Transaction class 0 (which we replace below) */
  197. static unsigned char data[] = { 0x0e, 0x00, 0x00, 0x00 };
  198. gw_assert(class >= 0);
  199. gw_assert(class <= 2);
  200. pdu = octstr_create_from_data(data, sizeof(data));
  201. octstr_set_char(pdu, 3, class);
  202. if (user_ack)
  203. set_user_ack(pdu);
  204. return pdu;
  205. }
  206. static Octstr *wtp_ack_create(void) {
  207. static unsigned char data[] = { 0x18, 0x00, 0x00 };
  208. return octstr_create_from_data(data, sizeof(data));
  209. }
  210. static void add_wsp_connect(Octstr *pdu) {
  211. static unsigned char data[] = { Connect_PDU, WSP_VERSION, 68, 0x00,
  212. 0x03, 0x80, 0x90, 0x00, 0x03, 0x81, 0x90, 0x00,
  213. 0x02, 0x82, 0x30, 0x02, 0x83, 0x01, 0x02, 0x84,
  214. 0x01, 0x28, 0x85, 0x50, 0x58, 0x2d, 0x55, 0x50,
  215. 0x2d, 0x41, 0x47, 0x45, 0x54, 0x00, 0x51, 0x58,
  216. 0x2d, 0x55, 0x50, 0x2d, 0x47, 0x45, 0x54, 0x4e,
  217. 0x4f, 0x54, 0x49, 0x46, 0x59, 0x00, 0x70, 0x58,
  218. 0x2d, 0x55, 0x50, 0x2d, 0x41, 0x50, 0x4f, 0x53,
  219. 0x54, 0x00, 0x09, 0x86, 0x02, 0x78, 0x2d, 0x75,
  220. 0x70, 0x2d, 0x31, 0x00 };
  221. octstr_append_data(pdu, data, sizeof(data));
  222. }
  223. static void add_wsp_get(Octstr *pdu) {
  224.      Octstr *urlbuf;
  225. octstr_append_char(pdu, Get_PDU);
  226. if (http_url) {
  227. octstr_append_uintvar(pdu, octstr_len(http_url));
  228. octstr_append(pdu, http_url);
  229. } else {
  230. urlbuf = octstr_format("http://localhost:%ld/hello.wml",
  231.        (long) http_port);
  232. octstr_append_uintvar(pdu, octstr_len(urlbuf));
  233. octstr_append(pdu, urlbuf);
  234. octstr_destroy(urlbuf);
  235. }
  236. }
  237. static void add_wsp_disconnect(Octstr *pdu, long session_id) {
  238. octstr_append_char(pdu, Disconnect_PDU);
  239. octstr_append_uintvar(pdu, session_id);
  240. }
  241. static void set_tid(Octstr *pdu, int tid) {
  242. int c;
  243. /* Tid wraps at 15 bits. */
  244. tid &= 0x7fff;
  245. c = octstr_get_char(pdu, 1);
  246. c = (tid >> 8) | (c & 0x80);
  247. octstr_set_char(pdu, 1, c);
  248. octstr_set_char(pdu, 2, tid & 0xff);
  249. }
  250. static int get_tid(Octstr *pdu) {
  251. return octstr_get_bits(pdu, 8, 16);
  252. }
  253. static int wtp_type(Octstr *pdu) {
  254. return octstr_get_bits(pdu, 1, 4);
  255. }
  256. static Msg *wdp_create(Octstr *data, Client *client) {
  257. Msg *msg;
  258. msg = msg_create(wdp_datagram);
  259. msg->wdp_datagram.source_address = octstr_create("127.0.0.1");
  260. msg->wdp_datagram.source_port = client->port;
  261. msg->wdp_datagram.destination_address = octstr_create("127.0.0.1");
  262. msg->wdp_datagram.destination_port = 9201;
  263. msg->wdp_datagram.user_data = octstr_duplicate(data);
  264. return msg;
  265. }
  266. static void send_pdu(Octstr *pdu, Connection *boxc, Client *client) {
  267. Msg *msg;
  268. Octstr *data;
  269. if (verbose_debug) {
  270. debug("test", 0, "Sending:");
  271. octstr_dump(pdu, 0);
  272. }
  273. msg = wdp_create(pdu, client);
  274. data = msg_pack(msg);
  275. conn_write_withlen(boxc, data);
  276. octstr_destroy(data);
  277. msg_destroy(msg);
  278. }
  279. static void send_invoke_connect(Connection *boxc, Client *client) {
  280. Octstr *pdu;
  281. gw_assert(client != NULL);
  282. gw_assert(client->wtp_invoked == 0);
  283. gw_assert(client->wsp_connected == 0);
  284. pdu = wtp_invoke_create(2);
  285. set_tid(pdu, client->wtp_tid);
  286. add_wsp_connect(pdu);
  287. send_pdu(pdu, boxc, client);
  288. octstr_destroy(pdu);
  289. client->wtp_invoked = 1;
  290. }
  291. static void send_invoke_get(Connection *boxc, Client *client) {
  292. Octstr *pdu;
  293. gw_assert(client != NULL);
  294. gw_assert(client->wtp_invoked == 0);
  295. gw_assert(client->wsp_connected == 1);
  296. pdu = wtp_invoke_create(2);
  297. set_tid(pdu, client->wtp_tid);
  298. add_wsp_get(pdu);
  299. send_pdu(pdu, boxc, client);
  300. octstr_destroy(pdu);
  301. client->wtp_invoked = 1;
  302. }
  303. static void record_disconnect(Client *client) {
  304. client->wsp_connected = 0;
  305. client->wsp_session_id = -1;
  306. client->pages_fetched = 0;
  307. increment_tid(client);
  308. }
  309. static void send_invoke_disconnect(Connection *boxc, Client *client) {
  310. Octstr *pdu;
  311. gw_assert(client != NULL);
  312. gw_assert(client->wtp_invoked == 0);
  313. gw_assert(client->wsp_connected == 1);
  314. /* Kannel can't handle it as class 1 yet, so send class 0 */
  315. pdu = wtp_invoke_create(0);
  316. set_tid(pdu, client->wtp_tid);
  317. add_wsp_disconnect(pdu, client->wsp_session_id);
  318. send_pdu(pdu, boxc, client);
  319. octstr_destroy(pdu);
  320. record_disconnect(client);
  321. client_done(client);
  322. }
  323. static void handle_connect_reply(Connection *boxc, Client *client, Octstr *pdu) {
  324. Octstr *ack;
  325. gw_assert(client);
  326. gw_assert(client->wtp_invoked);
  327. gw_assert(!client->wsp_connected);
  328. if (octstr_get_char(pdu, 3) != ConnectReply_PDU) {
  329. error(0, "Unexpected CONNECT reply");
  330. octstr_dump(pdu, 0);
  331. return;
  332. }
  333. ack = wtp_ack_create();
  334. set_tid(ack, client->wtp_tid);
  335. send_pdu(ack, boxc, client);
  336. octstr_destroy(ack);
  337. client->wtp_invoked = 0;
  338. increment_tid(client);
  339. client->wsp_connected = 1;
  340. client->wsp_session_id = get_varint(pdu, 4);
  341. send_invoke_get(boxc, client);
  342. }
  343. static void handle_get_reply(Connection *boxc, Client *client, Octstr *pdu) {
  344. Octstr *ack;
  345. gw_assert(client);
  346. gw_assert(client->wtp_invoked);
  347. gw_assert(client->wsp_connected);
  348. if (octstr_get_char(pdu, 3) != Reply_PDU) {
  349. error(0, "Unexpected GET reply");
  350. octstr_dump(pdu, 0);
  351. return;
  352. }
  353. ack = wtp_ack_create();
  354. set_tid(ack, client->wtp_tid);
  355. send_pdu(ack, boxc, client);
  356. octstr_destroy(ack);
  357. client->wtp_invoked = 0;
  358. increment_tid(client);
  359. client->pages_fetched++;
  360. if (client->pages_fetched == req_per_session) {
  361. send_invoke_disconnect(boxc, client);
  362. } else {
  363. client_done(client);
  364. }
  365. }
  366. static void handle_reply(Connection *boxc, Msg *reply) {
  367. Client *client;
  368. Octstr *wtp;
  369. int type;
  370. int dumped = 0;
  371. gw_assert(reply != NULL);
  372. gw_assert(reply->type == wdp_datagram);
  373. client = find_client(reply->wdp_datagram.destination_port);
  374. if (client == NULL)
  375. panic(0, "got packet for nonexisting client %ld",
  376. (long) reply->wdp_datagram.destination_port);
  377. wtp = reply->wdp_datagram.user_data;
  378. type = wtp_type(wtp);
  379. if (verbose_debug) {
  380. debug("test", 0, "Received:");
  381. octstr_dump(wtp, 0);
  382. dumped = 1;
  383. }
  384. if (client->wtp_invoked == 0) {
  385. error(0, "Got packet for client that wasn't waiting");
  386. if (!dumped) {
  387. octstr_dump(wtp, 0);
  388. dumped = 1;
  389. }
  390. return;
  391. }
  392. /* Server should invert the MSB of the tid in its replies */
  393. if (get_tid(wtp) != (client->wtp_tid ^ 0x8000)) {
  394. error(0, "Got packet with wrong tid %d, expected %d.",
  395. get_tid(wtp), client->wtp_tid ^ 0x8000);
  396. if (!dumped) {
  397. octstr_dump(wtp, 0);
  398. dumped = 1;
  399. }
  400. return;
  401. }
  402. /* We're going to be stupid here, and assume that replies that
  403.  * look vaguely like what we expect are actually what we wanted. */
  404. if (client->wsp_connected == 0 && type == RESULT) {
  405. handle_connect_reply(boxc, client, wtp);
  406. } else if (client->wsp_connected == 1 && type == RESULT) {
  407. handle_get_reply(boxc, client, wtp);
  408. } else if (client->wsp_connected == 2 && type == ACK) {
  409. record_disconnect(client);
  410. client_done(client);
  411. } else {
  412. error(0, "Got unexpected packet");
  413. if (!dumped) {
  414. octstr_dump(wtp, 0);
  415. dumped = 1;
  416. }
  417. }
  418. }
  419. static void start_request(Connection *boxc, Client *client) {
  420. gw_assert(client != NULL);
  421. gw_assert(client->wsp_connected != 2);
  422. gw_assert(client->wtp_invoked == 0);
  423. if (client->wsp_connected == 0) {
  424. send_invoke_connect(boxc, client); 
  425. } else {
  426. send_invoke_get(boxc, client);
  427. }
  428. }
  429. static long run_requests(Connection *boxc) {
  430. int requests_sent;
  431. Octstr *data;
  432. Msg *msg;
  433. int ret;
  434. requests_sent = 0;
  435. requests_complete = 0;
  436. while (requests_complete < max_requests) {
  437. data = conn_read_withlen(boxc);
  438. if (!data) {
  439. Client *client;
  440. if (requests_sent < max_requests
  441.     && (client = list_extract_first(ready_clients))) {
  442. start_request(boxc, client);
  443. requests_sent++;
  444. }
  445. ret = conn_wait(boxc, TIMEOUT);
  446. if (ret < 0 || conn_eof(boxc))
  447. panic(0, "Wapbox dead.");
  448. if (ret == 1)
  449. break; /* Timed out. */
  450. } else {
  451. msg = msg_unpack(data);
  452. if (!msg) {
  453. octstr_dump(data, 0);
  454. panic(0, "Received bad data from wapbox.");
  455. }
  456. if (msg->type == wdp_datagram)
  457. handle_reply(boxc, msg);
  458. msg_destroy(msg);
  459. }
  460. octstr_destroy(data);
  461. }
  462. if (requests_complete < max_requests)
  463. info(0, "Timeout.  %ld requests unsatisfied.",
  464. max_requests - requests_complete);
  465. return requests_complete;
  466. }
  467. static void help(void) {
  468. info(0, "Usage: drive_wapbox [options...]n");
  469. info(0, "  -r requests  Stop after this many; default 1.");
  470. info(0, "  -c clients   # of concurrent clients; default 1.");
  471. info(0, "  -w wapport   Port wapbox should connect to; default 30188");
  472. info(0, "  -u url       Use this url instead of internal http server");
  473. info(0, "  -g requests  Number of requests per WSP session; default 1");
  474. info(0, "  -U           Set the User ack flag on all WTP transactions");
  475. }
  476. int main(int argc, char **argv) {
  477. int opt;
  478. struct timeval start, end;
  479. Connection *boxc;
  480. long completed;
  481. double run_time;
  482. gwlib_init();
  483. while ((opt = getopt(argc, argv, "hv:r:c:w:du:Ug:")) != EOF) {
  484. switch (opt) {
  485. case 'v':
  486. log_set_output_level(atoi(optarg));
  487. break;
  488. case 'r':
  489. max_requests = atol(optarg);
  490. break;
  491. case 'c':
  492. max_clients = atol(optarg);
  493. break;
  494. case 'w':
  495. wapbox_port = atoi(optarg);
  496. break;
  497. case 'u':
  498. http_url = octstr_create(optarg);
  499. break;
  500. case 'U':
  501. user_ack = 1;
  502. break;
  503. case 'h':
  504. help();
  505. exit(0);
  506. case 'd':
  507. verbose_debug = 1;
  508. break;
  509. case 'g':
  510. req_per_session = atoi(optarg);
  511. break;
  512. case '?':
  513. default:
  514. error(0, "Invalid option %c", opt);
  515. help();
  516. panic(0, "Stopping.");
  517. }
  518. }
  519. if (!http_url)
  520. http_port = start_http_thread();
  521. boxc = start_wapbox();
  522. initialize_clients();
  523. if (gettimeofday(&start, NULL) < 0)
  524. panic(errno, "gettimeofday failed");
  525. completed = run_requests(boxc);
  526. if (gettimeofday(&end, NULL) < 0)
  527. panic(errno, "gettimeofday failed");
  528. conn_destroy(boxc);
  529. run_time = end.tv_sec - start.tv_sec;
  530. run_time += (double) (end.tv_usec - start.tv_usec) / 1000000.0;
  531. /* We must have timed out.  Don't count the waiting time. */
  532. if (completed < max_requests)
  533. run_time -= TIMEOUT;
  534. info(0, "%ld request%s in %0.1f seconds, %0.1f requests/s.",
  535. completed, completed != 1 ? "s" : "",
  536. run_time, max_requests / run_time);
  537. dying = 1;
  538. http_close_all_ports();
  539. if (!http_url)
  540. gwthread_join(http_thread_id);
  541. destroy_clients();
  542. octstr_destroy(http_url);
  543. gwlib_shutdown();
  544. return 0;
  545. }