1 /* This case tests basic GET method. */
2 #include "tx_api.h"
3 #include "nx_api.h"
4 #include "fx_api.h"
5 #include "nx_web_http_client.h"
6 #include "nx_web_http_server.h"
7
8 extern void test_control_return(UINT);
9
10 #if !defined(NX_DISABLE_IPV4)
11
12 #ifdef NX_WEB_HTTPS_ENABLE
13 #include "test_device_cert.c"
14 #include "test_ca_cert.c"
15 #define ca_cert_der test_ca_cert_der
16 #define ca_cert_der_len test_ca_cert_der_len
17 #endif /* NX_WEB_HTTPS_ENABLE */
18
19 #define DEMO_STACK_SIZE 4096
20
21 /* Set up FileX and file memory resources. */
22 static CHAR ram_disk_memory[4096];
23 static FX_MEDIA ram_disk;
24 static UCHAR media_memory[4096];
25
26 static UCHAR server_stack[16000];
27
28 /* Define device drivers. */
29 extern void _fx_ram_driver(FX_MEDIA *media_ptr);
30 extern void _nx_ram_network_driver_1024(NX_IP_DRIVER *driver_req_ptr);
31
32 /* Set up the HTTP client global variables. */
33
34 #define CLIENT_PACKET_SIZE (NX_WEB_HTTP_CLIENT_MIN_PACKET_SIZE * 2)
35
36 static TX_THREAD client_thread;
37 static NX_PACKET_POOL client_pool;
38 static NX_WEB_HTTP_CLIENT my_client;
39 static NX_IP client_ip;
40 static UINT error_counter;
41
42 /* Set up the HTTP server global variables */
43
44 #define SERVER_PACKET_SIZE (NX_WEB_HTTP_SERVER_MIN_PACKET_SIZE * 2)
45
46 static NX_WEB_HTTP_SERVER my_server;
47 static NX_PACKET_POOL server_pool;
48 static TX_THREAD server_thread;
49 static NX_IP server_ip;
50 static NXD_ADDRESS server_ip_address;
51 static UINT http_server_start = 0;
52 static UINT http_client_stop = 0;
53
54 static void thread_client_entry(ULONG thread_input);
55 static void thread_server_entry(ULONG thread_input);
56
57 #define HTTP_SERVER_ADDRESS IP_ADDRESS(1,2,3,4)
58 #define HTTP_CLIENT_ADDRESS IP_ADDRESS(1,2,3,5)
59
60 #ifdef NX_WEB_HTTPS_ENABLE
61 static UINT https_server_start = 0;
62 static UINT https_client_stop = 0;
63 static UINT loop = 4;
64 extern const NX_SECURE_TLS_CRYPTO nx_crypto_tls_ciphers;
65 static CHAR crypto_metadata_server[20000 * NX_WEB_HTTP_SERVER_SESSION_MAX];
66 static CHAR crypto_metadata_client[20000 * NX_WEB_HTTP_SERVER_SESSION_MAX];
67 static UCHAR tls_packet_buffer[18500];
68 static NX_SECURE_X509_CERT certificate;
69 static NX_SECURE_X509_CERT trusted_certificate;
70 static NX_SECURE_X509_CERT remote_certificate, remote_issuer;
71 static UCHAR remote_cert_buffer[2000];
72 static UCHAR remote_issuer_buffer[2000];
73
74 static UINT tls_setup_callback(NX_WEB_HTTP_CLIENT *client_ptr, NX_SECURE_TLS_SESSION *tls_session);
75 #else
76 static UINT loop = 2;
77 #endif /* NX_WEB_HTTPS_ENABLE */
78
79 static UINT authentication_check(NX_WEB_HTTP_SERVER *server_ptr, UINT request_type,
80 CHAR *resource, CHAR **name, CHAR **password, CHAR **realm);
81 static UINT server_request_callback(NX_WEB_HTTP_SERVER *server_ptr, UINT request_type, CHAR *resource, NX_PACKET *packet_ptr);
82
83 #ifdef CTEST
test_application_define(void * first_unused_memory)84 VOID test_application_define(void *first_unused_memory)
85 #else
86 void netx_web_basic_test_application_define(void *first_unused_memory)
87 #endif
88 {
89 CHAR *pointer;
90 UINT status;
91
92
93 error_counter = 0;
94
95 /* Setup the working pointer. */
96 pointer = (CHAR *) first_unused_memory;
97
98 /* Create a helper thread for the server. */
99 tx_thread_create(&server_thread, "HTTP Server thread", thread_server_entry, 0,
100 pointer, DEMO_STACK_SIZE,
101 NX_WEB_HTTP_SERVER_PRIORITY, NX_WEB_HTTP_SERVER_PRIORITY, TX_NO_TIME_SLICE, TX_AUTO_START);
102
103 pointer = pointer + DEMO_STACK_SIZE;
104
105 /* Initialize the NetX system. */
106 nx_system_initialize();
107
108 /* Create the server packet pool. */
109 status = nx_packet_pool_create(&server_pool, "HTTP Server Packet Pool", SERVER_PACKET_SIZE,
110 pointer, SERVER_PACKET_SIZE*8);
111 pointer = pointer + SERVER_PACKET_SIZE * 8;
112 if (status)
113 error_counter++;
114
115 /* Create an IP instance. */
116 status = nx_ip_create(&server_ip, "HTTP Server IP", HTTP_SERVER_ADDRESS,
117 0xFFFFFF00UL, &server_pool, _nx_ram_network_driver_1024,
118 pointer, 4096, 1);
119 pointer = pointer + 4096;
120 if (status)
121 error_counter++;
122
123 /* Enable ARP and supply ARP cache memory for the server IP instance. */
124 status = nx_arp_enable(&server_ip, (void *) pointer, 1024);
125 pointer = pointer + 1024;
126 if (status)
127 error_counter++;
128
129
130 /* Enable TCP traffic. */
131 status = nx_tcp_enable(&server_ip);
132 if (status)
133 error_counter++;
134
135 /* Create the HTTP Client thread. */
136 status = tx_thread_create(&client_thread, "HTTP Client", thread_client_entry, 0,
137 pointer, DEMO_STACK_SIZE,
138 NX_WEB_HTTP_SERVER_PRIORITY + 2, NX_WEB_HTTP_SERVER_PRIORITY + 2, TX_NO_TIME_SLICE, TX_AUTO_START);
139 pointer = pointer + DEMO_STACK_SIZE;
140 if (status)
141 error_counter++;
142
143 /* Create the Client packet pool. */
144 status = nx_packet_pool_create(&client_pool, "HTTP Client Packet Pool", CLIENT_PACKET_SIZE,
145 pointer, CLIENT_PACKET_SIZE*8);
146 pointer = pointer + CLIENT_PACKET_SIZE * 8;
147 if (status)
148 error_counter++;
149
150 /* Create an IP instance. */
151 status = nx_ip_create(&client_ip, "HTTP Client IP", HTTP_CLIENT_ADDRESS,
152 0xFFFFFF00UL, &client_pool, _nx_ram_network_driver_1024,
153 pointer, 2048, 1);
154 pointer = pointer + 2048;
155 if (status)
156 error_counter++;
157
158 status = nx_arp_enable(&client_ip, (void *) pointer, 1024);
159 pointer = pointer + 2048;
160 if (status)
161 error_counter++;
162
163 /* Enable TCP traffic. */
164 status = nx_tcp_enable(&client_ip);
165 if (status)
166 error_counter++;
167 }
168
169 #ifdef NX_WEB_HTTPS_ENABLE
170 /* Define the TLS setup callback function. */
tls_setup_callback(NX_WEB_HTTP_CLIENT * client_ptr,NX_SECURE_TLS_SESSION * tls_session)171 static UINT tls_setup_callback(NX_WEB_HTTP_CLIENT *client_ptr, NX_SECURE_TLS_SESSION *tls_session)
172 {
173 UINT status;
174
175
176 /* Initialize and create TLS session. */
177 status = nx_secure_tls_session_create(tls_session, &nx_crypto_tls_ciphers, crypto_metadata_client, sizeof(crypto_metadata_client));
178
179 /* Check status. */
180 if (status)
181 {
182 return(status);
183 }
184
185 /* Allocate space for packet reassembly. */
186 status = nx_secure_tls_session_packet_buffer_set(&(client_ptr -> nx_web_http_client_tls_session), tls_packet_buffer, sizeof(tls_packet_buffer));
187
188 /* Check status. */
189 if (status)
190 {
191 return(status);
192 }
193
194 /* Add a CA Certificate to our trusted store for verifying incoming server certificates. */
195 nx_secure_x509_certificate_initialize(&trusted_certificate, ca_cert_der, ca_cert_der_len, NX_NULL, 0, NULL, 0, NX_SECURE_X509_KEY_TYPE_NONE);
196 nx_secure_tls_trusted_certificate_add(&(client_ptr -> nx_web_http_client_tls_session), &trusted_certificate);
197
198 /* Need to allocate space for the certificate coming in from the remote host. */
199 nx_secure_tls_remote_certificate_allocate(&(client_ptr -> nx_web_http_client_tls_session), &remote_certificate, remote_cert_buffer, sizeof(remote_cert_buffer));
200 nx_secure_tls_remote_certificate_allocate(&(client_ptr -> nx_web_http_client_tls_session), &remote_issuer, remote_issuer_buffer, sizeof(remote_issuer_buffer));
201
202 return(NX_SUCCESS);
203 }
204 #endif /* NX_WEB_HTTPS_ENABLE */
205
http_response_callback(NX_WEB_HTTP_CLIENT * client_ptr,CHAR * field_name,UINT field_name_length,CHAR * field_value,UINT field_value_length)206 static VOID http_response_callback(NX_WEB_HTTP_CLIENT *client_ptr, CHAR *field_name, UINT field_name_length,
207 CHAR *field_value, UINT field_value_length)
208 {
209 if (memcmp(field_name, "Content-Type", field_name_length) == 0)
210 {
211 if (memcmp(field_value, "text/plain", field_value_length) != 0)
212 error_counter++;
213 }
214 else if(memcmp(field_name, "Content-Length", field_name_length) == 0)
215 {
216 if (memcmp(field_value, "12", field_value_length) != 0)
217 error_counter++;
218 }
219 #ifndef NX_WEB_HTTP_KEEPALIVE_DISABLE
220 else if(memcmp(field_name, "Connection", field_name_length) == 0)
221 {
222 if (memcmp(field_value, "keep-alive", field_value_length) != 0)
223 error_counter++;
224 }
225 #endif
226 }
227
thread_client_entry(ULONG thread_input)228 void thread_client_entry(ULONG thread_input)
229 {
230 UINT i;
231 UINT status;
232 NX_PACKET *recv_packet;
233
234
235 /* Give IP task and driver a chance to initialize the system. */
236 tx_thread_sleep(NX_IP_PERIODIC_RATE);
237
238 /* Set server IP address. */
239 server_ip_address.nxd_ip_address.v4 = HTTP_SERVER_ADDRESS;
240 server_ip_address.nxd_ip_version = NX_IP_VERSION_V4;
241
242 /* First loop test HTTP, second loop test HTTPS. */
243 for (i = 0; i < loop ; i++)
244 {
245 if (i < 2)
246 {
247
248 /* Wait HTTP server started. */
249 while(!http_server_start)
250 {
251 tx_thread_sleep(NX_IP_PERIODIC_RATE);
252 }
253 }
254 #ifdef NX_WEB_HTTPS_ENABLE
255 else
256 {
257
258 /* Wait HTTPS server started. */
259 while(!https_server_start)
260 {
261 tx_thread_sleep(NX_IP_PERIODIC_RATE);
262 }
263 }
264 #endif
265
266 /* Create an HTTP client instance. */
267 status = nx_web_http_client_create(&my_client, "HTTP Client", &client_ip, &client_pool, 1536);
268
269 /* Check status. */
270 if (status)
271 error_counter++;
272
273 /* Set the header callback routine. */
274 nx_web_http_client_response_header_callback_set(&my_client, http_response_callback);
275
276 /* Send a GET request. */
277 if (i == 0)
278 {
279 status = nx_web_http_client_get_start(&my_client, &server_ip_address,
280 NX_WEB_HTTP_SERVER_PORT, "http://1.2.3.4/test.txt",
281 "1.2.3.4", "name", "password", NX_WAIT_FOREVER);
282 }
283 else if (i == 1)
284 {
285 status = nx_web_http_client_get_start_extended(&my_client, &server_ip_address,
286 NX_WEB_HTTP_SERVER_PORT, "http://1.2.3.4/test.txt", sizeof("http://1.2.3.4/test.txt") - 1,
287 "1.2.3.4", 7, "name", 4, "password", 8, NX_WAIT_FOREVER);
288 }
289 #ifdef NX_WEB_HTTPS_ENABLE
290 else if (i == 2)
291 {
292 status = nx_web_http_client_get_secure_start(&my_client, &server_ip_address,
293 NX_WEB_HTTPS_SERVER_PORT, "https://1.2.3.4/test.txt",
294 "1.2.3.4", "name", "password",
295 tls_setup_callback, NX_WAIT_FOREVER);
296 }
297 else
298 {
299 status = nx_web_http_client_get_secure_start_extended(&my_client, &server_ip_address,
300 NX_WEB_HTTPS_SERVER_PORT, "https://1.2.3.4/test.txt", sizeof("https://1.2.3.4/test.txt") - 1,
301 "1.2.3.4", 7, "name", 4, "password", 8,
302 tls_setup_callback, NX_WAIT_FOREVER);
303 }
304 #endif /* NX_WEB_HTTPS_ENABLE */
305
306 /* Check status. */
307 if (status)
308 error_counter++;
309
310 /* Get response from server. */
311 while(1)
312 {
313 status = nx_web_http_client_response_body_get(&my_client, &recv_packet, 1 * NX_IP_PERIODIC_RATE);
314
315 if (status)
316 break;
317 else
318 nx_packet_release(recv_packet);
319 }
320
321 /* Check status. */
322 if (status != NX_WEB_HTTP_GET_DONE)
323 error_counter++;
324 else
325 nx_packet_release(recv_packet);
326
327 /* Connect to server. */
328 if (i < 2)
329 {
330 status = nx_web_http_client_connect(&my_client, &server_ip_address, NX_WEB_HTTP_SERVER_PORT, NX_WAIT_FOREVER);
331 }
332 #ifdef NX_WEB_HTTPS_ENABLE
333 else
334 {
335 status = nx_web_http_client_secure_connect(&my_client, &server_ip_address, NX_WEB_HTTPS_SERVER_PORT,
336 tls_setup_callback, NX_WAIT_FOREVER);
337 }
338 #endif /* NX_WEB_HTTPS_ENABLE */
339
340 /* Check status. */
341 if (status)
342 error_counter++;
343
344 if (i % 2)
345 {
346 /* Initialize the request. */
347 status = nx_web_http_client_request_initialize(&my_client, NX_WEB_HTTP_METHOD_GET, "/test.txt", "1.2.3.4",
348 0, NX_FALSE, "name", "password", NX_WAIT_FOREVER);
349 }
350 else
351 {
352 /* Initialize the request. */
353 status = nx_web_http_client_request_initialize_extended(&my_client, NX_WEB_HTTP_METHOD_GET, "/test.txt", 9, "1.2.3.4", 7,
354 0, NX_FALSE, "name", 4, "password", 8, NX_WAIT_FOREVER);
355 }
356
357 /* Check status. */
358 if (status)
359 error_counter++;
360
361 /* Send the request. */
362 status = nx_web_http_client_request_send(&my_client, NX_WAIT_FOREVER);
363
364 /* Check status. */
365 if (status)
366 error_counter++;
367
368 /* Get response from server. */
369 while(1)
370 {
371 status = nx_web_http_client_response_body_get(&my_client, &recv_packet, 1 * NX_IP_PERIODIC_RATE);
372
373 if (status)
374 break;
375 else
376 nx_packet_release(recv_packet);
377 }
378
379 /* Check status. */
380 if (status != NX_WEB_HTTP_GET_DONE)
381 error_counter++;
382 else
383 nx_packet_release(recv_packet);
384
385 status = nx_web_http_client_delete(&my_client);
386 if (status)
387 error_counter++;
388
389 /* Set the flag. */
390 if (i == 1)
391 {
392 http_client_stop = 1;
393 }
394 #ifdef NX_WEB_HTTPS_ENABLE
395 else if (i == 3)
396 {
397 https_client_stop = 1;
398 }
399 #endif /* NX_WEB_HTTPS_ENABLE */
400 }
401 }
402
403 /* Define the helper HTTP server thread. */
thread_server_entry(ULONG thread_input)404 void thread_server_entry(ULONG thread_input)
405 {
406 UINT i;
407 UINT status;
408 FX_FILE my_file;
409 UINT server_port = NX_WEB_HTTP_SERVER_PORT;
410
411
412 /* Print out test information banner. */
413 printf("NetX Test: Web Basic Test............................................");
414
415 /* Check for earlier error. */
416 if(error_counter)
417 {
418 printf("ERROR!\n");
419 test_control_return(1);
420 }
421
422 fx_media_format(&ram_disk,
423 _fx_ram_driver, // Driver entry
424 ram_disk_memory, // RAM disk memory pointer
425 media_memory, // Media buffer pointer
426 sizeof(media_memory), // Media buffer size
427 "MY_RAM_DISK", // Volume Name
428 1, // Number of FATs
429 32, // Directory Entries
430 0, // Hidden sectors
431 256, // Total sectors
432 512, // Sector size
433 8, // Sectors per cluster
434 1, // Heads
435 1); // Sectors per track
436
437 /* Open the RAM disk. */
438 status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, media_memory, sizeof(media_memory)) ;
439 status += fx_file_create(&ram_disk, "TEST.TXT");
440 status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE);
441 status += fx_file_write(&my_file, "https server", 12);
442 status += fx_file_close(&my_file);
443 if(status)
444 error_counter++;
445
446 /* Give NetX a chance to initialize the system. */
447 tx_thread_sleep(NX_IP_PERIODIC_RATE);
448
449 /* First loop test HTTP, second loop test HTTPS. */
450 for (i = 0; i < loop/2; i++)
451 {
452
453 if (i == 1)
454 {
455 server_port = NX_WEB_HTTPS_SERVER_PORT;
456 }
457
458 /* Create the HTTP Server. */
459 status = nx_web_http_server_create(&my_server, "My HTTP Server", &server_ip, server_port, &ram_disk,
460 &server_stack, sizeof(server_stack), &server_pool,
461 authentication_check, server_request_callback);
462 if (status)
463 error_counter++;
464
465 #ifdef NX_WEB_HTTPS_ENABLE
466 /* Set TLS for HTTPS. */
467 if (i == 1)
468 {
469 /* Initialize device certificate (used for all sessions in HTTPS server). */
470 memset(&certificate, 0, sizeof(certificate));
471 nx_secure_x509_certificate_initialize(&certificate, test_device_cert_der, test_device_cert_der_len, NX_NULL, 0, test_device_cert_key_der, test_device_cert_key_der_len, NX_SECURE_X509_KEY_TYPE_RSA_PKCS1_DER);
472
473 /* Setup TLS session data for the TCP server. */
474 status = nx_web_http_server_secure_configure(&my_server, &nx_crypto_tls_ciphers,
475 crypto_metadata_server, sizeof(crypto_metadata_server), tls_packet_buffer, sizeof(tls_packet_buffer),
476 &certificate, NX_NULL, 0, NX_NULL, 0, NX_NULL, 0);
477 if (status)
478 error_counter++;
479 }
480 #endif /* NX_WEB_HTTPS_ENABLE */
481
482 /* OK to start the HTTP Server. */
483 status = nx_web_http_server_start(&my_server);
484 if (status)
485 error_counter++;
486
487 /* Set the flag. */
488 if (i == 0)
489 {
490 http_server_start = 1;
491
492 /* Wait HTTP test finished. */
493 while(!http_client_stop)
494 {
495 tx_thread_sleep(NX_IP_PERIODIC_RATE);
496 }
497 }
498 #ifdef NX_WEB_HTTPS_ENABLE
499 else
500 {
501 https_server_start = 1;
502
503 /* Wait HTTPS test finished. */
504 while(!https_client_stop)
505 {
506 tx_thread_sleep(NX_IP_PERIODIC_RATE);
507 }
508 }
509 #endif /* NX_WEB_HTTPS_ENABLE */
510
511 status = nx_web_http_server_delete(&my_server);
512 if (status)
513 error_counter++;
514 }
515
516 /* Check packet pool. */
517 if (server_pool.nx_packet_pool_available != server_pool.nx_packet_pool_total)
518 {
519 error_counter++;
520 }
521
522 if (client_pool.nx_packet_pool_available != client_pool.nx_packet_pool_total)
523 {
524 error_counter++;
525 }
526
527 if(error_counter)
528 {
529 printf("ERROR!\n");
530 test_control_return(1);
531 }
532 else
533 {
534 printf("SUCCESS!\n");
535 test_control_return(0);
536 }
537 }
538
539 /* Define the application's authentication check. This is called by
540 the HTTP server whenever a new request is received. */
authentication_check(NX_WEB_HTTP_SERVER * server_ptr,UINT request_type,CHAR * resource,CHAR ** name,CHAR ** password,CHAR ** realm)541 static UINT authentication_check(NX_WEB_HTTP_SERVER *server_ptr, UINT request_type,
542 CHAR *resource, CHAR **name, CHAR **password, CHAR **realm)
543 {
544
545 /* Just use a simple name, password, and realm for all
546 requests and resources. */
547 *name = "name";
548 *password = "password";
549 *realm = "NetX Duo HTTP demo";
550
551 /* Request basic authentication. */
552 return(NX_WEB_HTTP_BASIC_AUTHENTICATE);
553 }
554
555 /* Define the server request callback function. */
server_request_callback(NX_WEB_HTTP_SERVER * server_ptr,UINT request_type,CHAR * resource,NX_PACKET * packet_ptr)556 static UINT server_request_callback(NX_WEB_HTTP_SERVER *server_ptr, UINT request_type, CHAR *resource, NX_PACKET *packet_ptr)
557 {
558 ULONG offset, length;
559 NX_PACKET *response_pkt;
560 UCHAR buffer[4000];
561 UINT status;
562 CHAR response[] = "Test response from server - uploaded file contents:\n";
563
564
565 memset(buffer, 0, sizeof(buffer));
566 length = 0;
567
568 /* Process multipart data. */
569 if(request_type == NX_WEB_HTTP_SERVER_POST_REQUEST)
570 {
571
572 /* Get the content header. */
573 while(nx_web_http_server_get_entity_header(server_ptr, &packet_ptr, buffer,
574 sizeof(buffer)) == NX_SUCCESS)
575 {
576
577 /* Header obtained successfully. Get the content data location. */
578 while(nx_web_http_server_get_entity_content(server_ptr, &packet_ptr, &offset,
579 &length) == NX_SUCCESS)
580 {
581 /* make sure we don't overwrite our buffer! */
582 if(length > sizeof(buffer))
583 {
584 length = sizeof(buffer);
585 }
586
587 /* Write content data to buffer. */
588 nx_packet_data_extract_offset(packet_ptr, offset, buffer, length,
589 &length);
590 buffer[length] = 0;
591 }
592 }
593
594 /* Generate HTTP header. */
595 status = nx_web_http_server_callback_generate_response_header(server_ptr,
596 &response_pkt, NX_WEB_HTTP_STATUS_OK, 800, "text/html",
597 "Server: NetX HTTPS Experimental\r\n");
598
599 if(status)
600 {
601 return(status);
602 }
603
604 status = nx_packet_data_append(response_pkt, response, strlen(response),
605 &server_pool, NX_WAIT_FOREVER);
606
607 if(length > 0)
608 {
609 /* Only send what is in the buffer. */
610 if(length > 4000)
611 {
612 length = 4000;
613 }
614 status = nx_packet_data_append(response_pkt, buffer, length,
615 &server_pool, NX_WAIT_FOREVER);
616 }
617
618 if(status == NX_SUCCESS)
619 {
620 if(nx_web_http_server_callback_packet_send(server_ptr, response_pkt) != NX_SUCCESS)
621 {
622 nx_packet_release(response_pkt);
623 }
624 }
625 }
626 else
627 {
628 /* Indicate we have not processed the response to client yet. */
629 return(NX_SUCCESS);
630 }
631
632 /* Indicate the response to client is transmitted. */
633 return(NX_WEB_HTTP_CALLBACK_COMPLETED);
634 }
635 #else
636
637 #ifdef CTEST
test_application_define(void * first_unused_memory)638 VOID test_application_define(void *first_unused_memory)
639 #else
640 void netx_web_basic_test_application_define(void *first_unused_memory)
641 #endif
642 {
643
644 /* Print out test information banner. */
645 printf("NetX Test: Web Basic Test............................................N/A\n");
646
647 test_control_return(3);
648 }
649 #endif
650
651