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