1 /** @file
2 
3   Implements unit test for SDK APIs
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 // Turn off -Wdeprecated so that we can still test our own deprecated APIs.
25 #if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
26 #pragma GCC diagnostic ignored "-Wdeprecated"
27 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
28 #endif
29 
30 #include <sys/types.h>
31 #include <arpa/inet.h> /* For htonl */
32 
33 #include <cerrno>
34 #include <pthread.h>
35 #include <unistd.h>
36 #include <cstdio>
37 #include <cstring>
38 
39 #include "tscore/ink_config.h"
40 #include "tscore/ink_sprintf.h"
41 #include "tscore/ink_file.h"
42 #include "tscore/Regression.h"
43 #include "ts/ts.h"
44 #include "ts/experimental.h"
45 #include "records/I_RecCore.h"
46 
47 #include "P_Net.h"
48 #include "records/I_RecHttp.h"
49 
50 #include "http/HttpSM.h"
51 #include "tscore/TestBox.h"
52 
53 // This used to be in InkAPITestTool.cc, which we'd just #include here... But that seemed silly.
54 #define SDBG_TAG "SockServer"
55 #define CDBG_TAG "SockClient"
56 
57 #define IP(a, b, c, d) htonl((a) << 24 | (b) << 16 | (c) << 8 | (d))
58 
59 #define SET_TEST_HANDLER(_d, _s) \
60   {                              \
61     _d = _s;                     \
62   }
63 
64 #define MAGIC_ALIVE 0xfeedbaba
65 #define MAGIC_DEAD 0xdeadbeef
66 
67 #define SYNSERVER_LISTEN_PORT 3300
68 #define SYNSERVER_DUMMY_PORT -1
69 
70 #define PROXY_CONFIG_NAME_HTTP_PORT "proxy.config.http.server_port"
71 #define PROXY_HTTP_DEFAULT_PORT 8080
72 
73 #define REQUEST_MAX_SIZE 4095
74 #define RESPONSE_MAX_SIZE 4095
75 
76 #define HTTP_REQUEST_END "\r\n\r\n"
77 
78 // each request/response includes an identifier as a Mime field
79 #define X_REQUEST_ID "X-Request-ID"
80 #define X_RESPONSE_ID "X-Response-ID"
81 
82 #define ERROR_BODY "TESTING ERROR PAGE"
83 #define TRANSFORM_APPEND_STRING "This is a transformed response"
84 
85 //////////////////////////////////////////////////////////////////////////////
86 // STRUCTURES
87 //////////////////////////////////////////////////////////////////////////////
88 
89 using TxnHandler = int (*)(TSCont, TSEvent, void *);
90 
91 /* Server transaction structure */
92 typedef struct {
93   TSVConn vconn;
94 
95   TSVIO read_vio;
96   TSIOBuffer req_buffer;
97   TSIOBufferReader req_reader;
98 
99   TSVIO write_vio;
100   TSIOBuffer resp_buffer;
101   TSIOBufferReader resp_reader;
102 
103   char request[REQUEST_MAX_SIZE + 1];
104   int request_len;
105 
106   TxnHandler current_handler;
107   unsigned int magic;
108 } ServerTxn;
109 
110 /* Server structure */
111 typedef struct {
112   int accept_port;
113   TSAction accept_action;
114   TSCont accept_cont;
115   unsigned int magic;
116 } SocketServer;
117 
118 typedef enum {
119   REQUEST_SUCCESS,
120   REQUEST_INPROGRESS,
121   REQUEST_FAILURE,
122 } RequestStatus;
123 
124 /* Client structure */
125 typedef struct {
126   TSVConn vconn;
127 
128   TSVIO read_vio;
129   TSIOBuffer req_buffer;
130   TSIOBufferReader req_reader;
131 
132   TSVIO write_vio;
133   TSIOBuffer resp_buffer;
134   TSIOBufferReader resp_reader;
135 
136   char *request;
137   char response[RESPONSE_MAX_SIZE + 1];
138   int response_len;
139 
140   RequestStatus status;
141 
142   int connect_port;
143   int local_port;
144   uint64_t connect_ip;
145   TSAction connect_action;
146 
147   TxnHandler current_handler;
148 
149   unsigned int magic;
150 } ClientTxn;
151 
152 //////////////////////////////////////////////////////////////////////////////
153 // DECLARATIONS
154 //////////////////////////////////////////////////////////////////////////////
155 
156 /* utility */
157 static char *get_body_ptr(const char *request);
158 static char *generate_request(int test_case);
159 static char *generate_response(const char *request);
160 static int get_request_id(TSHttpTxn txnp);
161 
162 /* client side */
163 static ClientTxn *synclient_txn_create();
164 static int synclient_txn_delete(ClientTxn *txn);
165 static void synclient_txn_close(ClientTxn *txn);
166 static int synclient_txn_send_request(ClientTxn *txn, char *request);
167 static int synclient_txn_send_request_to_vc(ClientTxn *txn, char *request, TSVConn vc);
168 static int synclient_txn_read_response(TSCont contp);
169 static int synclient_txn_read_response_handler(TSCont contp, TSEvent event, void *data);
170 static int synclient_txn_write_request(TSCont contp);
171 static int synclient_txn_write_request_handler(TSCont contp, TSEvent event, void *data);
172 static int synclient_txn_connect_handler(TSCont contp, TSEvent event, void *data);
173 static int synclient_txn_main_handler(TSCont contp, TSEvent event, void *data);
174 
175 /* Server side */
176 SocketServer *synserver_create(int port);
177 static int synserver_start(SocketServer *s);
178 static int synserver_stop(SocketServer *s);
179 static int synserver_delete(SocketServer *s);
180 static int synserver_vc_accept(TSCont contp, TSEvent event, void *data);
181 static int synserver_vc_refuse(TSCont contp, TSEvent event, void *data);
182 static int synserver_txn_close(TSCont contp);
183 static int synserver_txn_write_response(TSCont contp);
184 static int synserver_txn_write_response_handler(TSCont contp, TSEvent event, void *data);
185 static int synserver_txn_read_request(TSCont contp);
186 static int synserver_txn_read_request_handler(TSCont contp, TSEvent event, void *data);
187 static int synserver_txn_main_handler(TSCont contp, TSEvent event, void *data);
188 
189 //////////////////////////////////////////////////////////////////////////////
190 // REQUESTS/RESPONSES GENERATION
191 //////////////////////////////////////////////////////////////////////////////
192 
193 static char *
194 get_body_ptr(const char *request)
195 {
196   char *ptr = const_cast<char *>(strstr(request, (const char *)"\r\n\r\n"));
197   return (ptr != nullptr) ? (ptr + 4) : nullptr;
198 }
199 
200 /* Caller must free returned request */
201 static char *
202 generate_request(int test_case)
203 {
204 // We define request formats.
205 // Each format has an X-Request-ID field that contains the id of the testcase
206 #define HTTP_REQUEST_DEFAULT_FORMAT                   \
207   "GET http://127.0.0.1:%d/default.html HTTP/1.0\r\n" \
208   "X-Request-ID: %d\r\n"                              \
209   "\r\n"
210 
211 #define HTTP_REQUEST_FORMAT1                          \
212   "GET http://127.0.0.1:%d/format1.html HTTP/1.0\r\n" \
213   "X-Request-ID: %d\r\n"                              \
214   "\r\n"
215 
216 #define HTTP_REQUEST_FORMAT2                          \
217   "GET http://127.0.0.1:%d/format2.html HTTP/1.0\r\n" \
218   "X-Request-ID: %d\r\n"                              \
219   "Content-Type: text/html\r\n"                       \
220   "\r\n"
221 #define HTTP_REQUEST_FORMAT3                          \
222   "GET http://127.0.0.1:%d/format3.html HTTP/1.0\r\n" \
223   "X-Request-ID: %d\r\n"                              \
224   "Response: Error\r\n"                               \
225   "\r\n"
226 #define HTTP_REQUEST_FORMAT4                          \
227   "GET http://127.0.0.1:%d/format4.html HTTP/1.0\r\n" \
228   "X-Request-ID: %d\r\n"                              \
229   "Request:%d\r\n"                                    \
230   "\r\n"
231 #define HTTP_REQUEST_FORMAT5                          \
232   "GET http://127.0.0.1:%d/format5.html HTTP/1.0\r\n" \
233   "X-Request-ID: %d\r\n"                              \
234   "Request:%d\r\n"                                    \
235   "\r\n"
236 #define HTTP_REQUEST_FORMAT6                         \
237   "GET http://127.0.0.1:%d/format.html HTTP/1.0\r\n" \
238   "X-Request-ID: %d\r\n"                             \
239   "Accept-Language: English\r\n"                     \
240   "\r\n"
241 #define HTTP_REQUEST_FORMAT7                         \
242   "GET http://127.0.0.1:%d/format.html HTTP/1.0\r\n" \
243   "X-Request-ID: %d\r\n"                             \
244   "Accept-Language: French\r\n"                      \
245   "\r\n"
246 #define HTTP_REQUEST_FORMAT8                         \
247   "GET http://127.0.0.1:%d/format.html HTTP/1.0\r\n" \
248   "X-Request-ID: %d\r\n"                             \
249   "Accept-Language: English,French\r\n"              \
250   "\r\n"
251 #define HTTP_REQUEST_FORMAT9                                      \
252   "GET http://trafficserver.apache.org/format9.html HTTP/1.0\r\n" \
253   "X-Request-ID: %d\r\n"                                          \
254   "\r\n"
255 #define HTTP_REQUEST_FORMAT10                                      \
256   "GET http://trafficserver.apache.org/format10.html HTTP/1.0\r\n" \
257   "X-Request-ID: %d\r\n"                                           \
258   "\r\n"
259 #define HTTP_REQUEST_FORMAT11                                      \
260   "GET http://trafficserver.apache.org/format11.html HTTP/1.0\r\n" \
261   "X-Request-ID: %d\r\n"                                           \
262   "\r\n"
263   char *request = static_cast<char *>(TSmalloc(REQUEST_MAX_SIZE + 1));
264 
265   switch (test_case) {
266   case 1:
267     snprintf(request, REQUEST_MAX_SIZE + 1, HTTP_REQUEST_FORMAT1, SYNSERVER_LISTEN_PORT, test_case);
268     break;
269   case 2:
270     snprintf(request, REQUEST_MAX_SIZE + 1, HTTP_REQUEST_FORMAT2, SYNSERVER_LISTEN_PORT, test_case);
271     break;
272   case 3:
273     snprintf(request, REQUEST_MAX_SIZE + 1, HTTP_REQUEST_FORMAT3, SYNSERVER_LISTEN_PORT, test_case);
274     break;
275   case 4:
276     snprintf(request, REQUEST_MAX_SIZE + 1, HTTP_REQUEST_FORMAT4, SYNSERVER_LISTEN_PORT, test_case, 1);
277     break;
278   case 5:
279     snprintf(request, REQUEST_MAX_SIZE + 1, HTTP_REQUEST_FORMAT5, SYNSERVER_LISTEN_PORT, test_case, 2);
280     break;
281   case 6:
282     snprintf(request, REQUEST_MAX_SIZE + 1, HTTP_REQUEST_FORMAT6, SYNSERVER_LISTEN_PORT, test_case);
283     break;
284   case 7:
285     snprintf(request, REQUEST_MAX_SIZE + 1, HTTP_REQUEST_FORMAT7, SYNSERVER_LISTEN_PORT, test_case - 1);
286     break;
287   case 8:
288     snprintf(request, REQUEST_MAX_SIZE + 1, HTTP_REQUEST_FORMAT8, SYNSERVER_LISTEN_PORT, test_case - 2);
289     break;
290   case 9:
291     snprintf(request, REQUEST_MAX_SIZE + 1, HTTP_REQUEST_FORMAT9, test_case);
292     break;
293   case 10:
294     snprintf(request, REQUEST_MAX_SIZE + 1, HTTP_REQUEST_FORMAT10, test_case);
295     break;
296   case 11:
297     snprintf(request, REQUEST_MAX_SIZE + 1, HTTP_REQUEST_FORMAT11, test_case);
298     break;
299   default:
300     snprintf(request, REQUEST_MAX_SIZE + 1, HTTP_REQUEST_DEFAULT_FORMAT, SYNSERVER_LISTEN_PORT, test_case);
301     break;
302   }
303 
304   return request;
305 }
306 
307 /* Caller must free returned response */
308 static char *
309 generate_response(const char *request)
310 {
311 // define format for response
312 // Each response contains a field X-Response-ID that contains the id of the testcase
313 #define HTTP_REQUEST_TESTCASE_FORMAT \
314   "GET %1024s HTTP/1.%d\r\n"         \
315   "X-Request-ID: %d\r\n"
316 
317 #define HTTP_RESPONSE_DEFAULT_FORMAT \
318   "HTTP/1.0 200 OK\r\n"              \
319   "X-Response-ID: %d\r\n"            \
320   "Cache-Control: max-age=86400\r\n" \
321   "Content-Type: text/html\r\n"      \
322   "\r\n"                             \
323   "Default body"
324 
325 #define HTTP_RESPONSE_FORMAT1   \
326   "HTTP/1.0 200 OK\r\n"         \
327   "X-Response-ID: %d\r\n"       \
328   "Content-Type: text/html\r\n" \
329   "Cache-Control: no-cache\r\n" \
330   "\r\n"                        \
331   "Body for response 1"
332 
333 #define HTTP_RESPONSE_FORMAT2        \
334   "HTTP/1.0 200 OK\r\n"              \
335   "X-Response-ID: %d\r\n"            \
336   "Cache-Control: max-age=86400\r\n" \
337   "Content-Type: text/html\r\n"      \
338   "\r\n"                             \
339   "Body for response 2"
340 #define HTTP_RESPONSE_FORMAT4        \
341   "HTTP/1.0 200 OK\r\n"              \
342   "X-Response-ID: %d\r\n"            \
343   "Cache-Control: max-age=86400\r\n" \
344   "Content-Type: text/html\r\n"      \
345   "\r\n"                             \
346   "Body for response 4"
347 #define HTTP_RESPONSE_FORMAT5   \
348   "HTTP/1.0 200 OK\r\n"         \
349   "X-Response-ID: %d\r\n"       \
350   "Content-Type: text/html\r\n" \
351   "\r\n"                        \
352   "Body for response 5"
353 #define HTTP_RESPONSE_FORMAT6        \
354   "HTTP/1.0 200 OK\r\n"              \
355   "X-Response-ID: %d\r\n"            \
356   "Cache-Control: max-age=86400\r\n" \
357   "Content-Language: English\r\n"    \
358   "\r\n"                             \
359   "Body for response 6"
360 #define HTTP_RESPONSE_FORMAT7        \
361   "HTTP/1.0 200 OK\r\n"              \
362   "X-Response-ID: %d\r\n"            \
363   "Cache-Control: max-age=86400\r\n" \
364   "Content-Language: French\r\n"     \
365   "\r\n"                             \
366   "Body for response 7"
367 
368 #define HTTP_RESPONSE_FORMAT8             \
369   "HTTP/1.0 200 OK\r\n"                   \
370   "X-Response-ID: %d\r\n"                 \
371   "Cache-Control: max-age=86400\r\n"      \
372   "Content-Language: French, English\r\n" \
373   "\r\n"                                  \
374   "Body for response 8"
375 
376 #define HTTP_RESPONSE_FORMAT9        \
377   "HTTP/1.0 200 OK\r\n"              \
378   "Cache-Control: max-age=86400\r\n" \
379   "X-Response-ID: %d\r\n"            \
380   "\r\n"                             \
381   "Body for response 9"
382 
383 #define HTTP_RESPONSE_FORMAT10       \
384   "HTTP/1.0 200 OK\r\n"              \
385   "Cache-Control: max-age=86400\r\n" \
386   "X-Response-ID: %d\r\n"            \
387   "\r\n"                             \
388   "Body for response 10"
389 
390 #define HTTP_RESPONSE_FORMAT11          \
391   "HTTP/1.0 200 OK\r\n"                 \
392   "Cache-Control: private,no-store\r\n" \
393   "X-Response-ID: %d\r\n"               \
394   "\r\n"                                \
395   "Body for response 11"
396 
397   int test_case, match, http_version;
398 
399   char *response = static_cast<char *>(TSmalloc(RESPONSE_MAX_SIZE + 1));
400   char url[1025];
401 
402   // coverity[secure_coding]
403   match = sscanf(request, HTTP_REQUEST_TESTCASE_FORMAT, url, &http_version, &test_case);
404   if (match == 3) {
405     switch (test_case) {
406     case 1:
407       snprintf(response, RESPONSE_MAX_SIZE + 1, HTTP_RESPONSE_FORMAT1, test_case);
408       break;
409     case 2:
410       snprintf(response, RESPONSE_MAX_SIZE + 1, HTTP_RESPONSE_FORMAT2, test_case);
411       break;
412     case 4:
413       snprintf(response, RESPONSE_MAX_SIZE + 1, HTTP_RESPONSE_FORMAT4, test_case);
414       break;
415     case 5:
416       snprintf(response, RESPONSE_MAX_SIZE + 1, HTTP_RESPONSE_FORMAT5, test_case);
417       break;
418     case 6:
419       snprintf(response, RESPONSE_MAX_SIZE + 1, HTTP_RESPONSE_FORMAT6, test_case);
420       break;
421     case 7:
422       snprintf(response, RESPONSE_MAX_SIZE + 1, HTTP_RESPONSE_FORMAT7, test_case);
423       break;
424     case 8:
425       snprintf(response, RESPONSE_MAX_SIZE + 1, HTTP_RESPONSE_FORMAT8, test_case);
426       break;
427     case 9:
428       snprintf(response, RESPONSE_MAX_SIZE + 1, HTTP_RESPONSE_FORMAT9, test_case);
429       break;
430     case 10:
431       snprintf(response, RESPONSE_MAX_SIZE + 1, HTTP_RESPONSE_FORMAT10, test_case);
432       break;
433     case 11:
434       snprintf(response, RESPONSE_MAX_SIZE + 1, HTTP_RESPONSE_FORMAT11, test_case);
435       break;
436     default:
437       snprintf(response, RESPONSE_MAX_SIZE + 1, HTTP_RESPONSE_DEFAULT_FORMAT, test_case);
438       break;
439     }
440   } else {
441     /* Didn't recognize a testcase request. send the default response */
442     snprintf(response, RESPONSE_MAX_SIZE + 1, HTTP_RESPONSE_DEFAULT_FORMAT, test_case);
443   }
444 
445   return response;
446 }
447 
448 static int
449 get_request_id_value(const char *name, TSMBuffer buf, TSMLoc hdr)
450 {
451   int id = -1;
452   TSMLoc field;
453 
454   field = TSMimeHdrFieldFind(buf, hdr, name, -1);
455   if (field != TS_NULL_MLOC) {
456     id = TSMimeHdrFieldValueIntGet(buf, hdr, field, 0);
457   }
458 
459   TSHandleMLocRelease(buf, hdr, field);
460   return id;
461 }
462 
463 // This routine can be called by tests, from the READ_REQUEST_HDR_HOOK
464 // to figure out the id of a test message
465 // Returns id/-1 in case of error
466 static int
467 get_request_id(TSHttpTxn txnp)
468 {
469   TSMBuffer bufp;
470   TSMLoc hdr_loc;
471   int id = -1;
472 
473   if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
474     return -1;
475   }
476 
477   id = get_request_id_value(X_REQUEST_ID, bufp, hdr_loc);
478   TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
479   return id;
480 }
481 
482 // This routine can be called by tests, from the READ_RESPONSE_HDR_HOOK
483 // to figure out the id of a test message
484 // Returns id/-1 in case of error
485 static int
486 get_response_id(TSHttpTxn txnp)
487 {
488   TSMBuffer bufp;
489   TSMLoc hdr_loc;
490   int id = -1;
491 
492   if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
493     return -1;
494   }
495 
496   id = get_request_id_value(X_RESPONSE_ID, bufp, hdr_loc);
497   TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
498   return id;
499 }
500 
501 //////////////////////////////////////////////////////////////////////////////
502 // SOCKET CLIENT
503 //////////////////////////////////////////////////////////////////////////////
504 
505 static ClientTxn *
506 synclient_txn_create()
507 {
508   const HttpProxyPort *proxy_port;
509 
510   ClientTxn *txn = static_cast<ClientTxn *>(TSmalloc(sizeof(ClientTxn)));
511 
512   ink_zero(*txn);
513 
514   if (nullptr == (proxy_port = HttpProxyPort::findHttp(AF_INET))) {
515     txn->connect_port = PROXY_HTTP_DEFAULT_PORT;
516   } else {
517     txn->connect_port = proxy_port->m_port;
518   }
519 
520   txn->connect_ip = IP(127, 0, 0, 1);
521   txn->status     = REQUEST_INPROGRESS;
522   txn->magic      = MAGIC_ALIVE;
523 
524   TSDebug(CDBG_TAG, "Connecting to proxy 127.0.0.1 on port %d", txn->connect_port);
525   return txn;
526 }
527 
528 static int
529 synclient_txn_delete(ClientTxn *txn)
530 {
531   TSAssert(txn->magic == MAGIC_ALIVE);
532   if (txn->connect_action && !TSActionDone(txn->connect_action)) {
533     TSActionCancel(txn->connect_action);
534     txn->connect_action = nullptr;
535   }
536 
537   ats_free(txn->request);
538   txn->magic = MAGIC_DEAD;
539   TSfree(txn);
540   return 1;
541 }
542 
543 static void
544 synclient_txn_close(ClientTxn *txn)
545 {
546   if (txn) {
547     if (txn->vconn != nullptr) {
548       TSVConnClose(txn->vconn);
549       txn->vconn = nullptr;
550     }
551 
552     if (txn->req_buffer != nullptr) {
553       TSIOBufferDestroy(txn->req_buffer);
554       txn->req_buffer = nullptr;
555     }
556 
557     if (txn->resp_buffer != nullptr) {
558       TSIOBufferDestroy(txn->resp_buffer);
559       txn->resp_buffer = nullptr;
560     }
561 
562     TSDebug(CDBG_TAG, "Client Txn destroyed");
563   }
564 }
565 
566 static int
567 synclient_txn_send_request(ClientTxn *txn, char *request)
568 {
569   TSCont cont;
570   sockaddr_in addr;
571 
572   TSAssert(txn->magic == MAGIC_ALIVE);
573   txn->request = ats_strdup(request);
574   SET_TEST_HANDLER(txn->current_handler, synclient_txn_connect_handler);
575 
576   cont = TSContCreate(synclient_txn_main_handler, TSMutexCreate());
577   TSContDataSet(cont, txn);
578 
579   ats_ip4_set(&addr, txn->connect_ip, htons(txn->connect_port));
580   TSNetConnect(cont, ats_ip_sa_cast(&addr));
581   return 1;
582 }
583 
584 /* This can be used to send a request to a specific VC */
585 static int
586 synclient_txn_send_request_to_vc(ClientTxn *txn, char *request, TSVConn vc)
587 {
588   TSCont cont;
589   TSAssert(txn->magic == MAGIC_ALIVE);
590   txn->request = ats_strdup(request);
591   SET_TEST_HANDLER(txn->current_handler, synclient_txn_connect_handler);
592 
593   cont = TSContCreate(synclient_txn_main_handler, TSMutexCreate());
594   TSContDataSet(cont, txn);
595 
596   TSContCall(cont, TS_EVENT_NET_CONNECT, vc);
597   return 1;
598 }
599 
600 static int
601 synclient_txn_read_response(TSCont contp)
602 {
603   ClientTxn *txn = static_cast<ClientTxn *>(TSContDataGet(contp));
604   TSAssert(txn->magic == MAGIC_ALIVE);
605 
606   TSIOBufferBlock block = TSIOBufferReaderStart(txn->resp_reader);
607   while (block != nullptr) {
608     int64_t blocklen;
609     const char *blockptr = TSIOBufferBlockReadStart(block, txn->resp_reader, &blocklen);
610 
611     if (txn->response_len + blocklen <= RESPONSE_MAX_SIZE) {
612       memcpy((txn->response + txn->response_len), blockptr, blocklen);
613       txn->response_len += blocklen;
614     } else {
615       TSError("Error: Response length %" PRId64 " > response buffer size %d", txn->response_len + blocklen, RESPONSE_MAX_SIZE);
616     }
617 
618     block = TSIOBufferBlockNext(block);
619   }
620 
621   txn->response[txn->response_len] = '\0';
622   TSDebug(CDBG_TAG, "Response = |%s|, req len = %d", txn->response, txn->response_len);
623 
624   return 1;
625 }
626 
627 static int
628 synclient_txn_read_response_handler(TSCont contp, TSEvent event, void * /* data ATS_UNUSED */)
629 {
630   ClientTxn *txn = static_cast<ClientTxn *>(TSContDataGet(contp));
631   TSAssert(txn->magic == MAGIC_ALIVE);
632 
633   int64_t avail;
634 
635   switch (event) {
636   case TS_EVENT_VCONN_READ_READY:
637   case TS_EVENT_VCONN_READ_COMPLETE:
638     if (event == TS_EVENT_VCONN_READ_READY) {
639       TSDebug(CDBG_TAG, "READ_READY");
640     } else {
641       TSDebug(CDBG_TAG, "READ_COMPLETE");
642     }
643 
644     avail = TSIOBufferReaderAvail(txn->resp_reader);
645     TSDebug(CDBG_TAG, "%" PRId64 " bytes available in buffer", avail);
646 
647     if (avail > 0) {
648       synclient_txn_read_response(contp);
649       TSIOBufferReaderConsume(txn->resp_reader, avail);
650     }
651 
652     TSVIOReenable(txn->read_vio);
653     break;
654 
655   case TS_EVENT_VCONN_EOS:
656     TSDebug(CDBG_TAG, "READ_EOS");
657     // Connection closed. In HTTP/1.0 it means we're done for this request.
658     txn->status = REQUEST_SUCCESS;
659     synclient_txn_close(static_cast<ClientTxn *>(TSContDataGet(contp)));
660     TSContDestroy(contp);
661     return 1;
662 
663   case TS_EVENT_ERROR:
664     TSDebug(CDBG_TAG, "READ_ERROR");
665     txn->status = REQUEST_FAILURE;
666     synclient_txn_close(static_cast<ClientTxn *>(TSContDataGet(contp)));
667     TSContDestroy(contp);
668     return 1;
669 
670   default:
671     TSAssert(!"Invalid event");
672     break;
673   }
674   return 1;
675 }
676 
677 static int
678 synclient_txn_write_request(TSCont contp)
679 {
680   ClientTxn *txn = static_cast<ClientTxn *>(TSContDataGet(contp));
681   TSAssert(txn->magic == MAGIC_ALIVE);
682 
683   TSIOBufferBlock block;
684   char *ptr_block;
685   int64_t len, ndone, ntodo, towrite, avail;
686 
687   len = strlen(txn->request);
688 
689   ndone = 0;
690   ntodo = len;
691   while (ntodo > 0) {
692     block     = TSIOBufferStart(txn->req_buffer);
693     ptr_block = TSIOBufferBlockWriteStart(block, &avail);
694     towrite   = std::min(ntodo, avail);
695     memcpy(ptr_block, txn->request + ndone, towrite);
696     TSIOBufferProduce(txn->req_buffer, towrite);
697     ntodo -= towrite;
698     ndone += towrite;
699   }
700 
701   /* Start writing the response */
702   TSDebug(CDBG_TAG, "Writing |%s| (%" PRId64 ") bytes", txn->request, len);
703   txn->write_vio = TSVConnWrite(txn->vconn, contp, txn->req_reader, len);
704 
705   return 1;
706 }
707 
708 static int
709 synclient_txn_write_request_handler(TSCont contp, TSEvent event, void * /* data ATS_UNUSED */)
710 {
711   ClientTxn *txn = static_cast<ClientTxn *>(TSContDataGet(contp));
712   TSAssert(txn->magic == MAGIC_ALIVE);
713 
714   switch (event) {
715   case TS_EVENT_VCONN_WRITE_READY:
716     TSDebug(CDBG_TAG, "WRITE_READY");
717     TSVIOReenable(txn->write_vio);
718     break;
719 
720   case TS_EVENT_VCONN_WRITE_COMPLETE:
721     TSDebug(CDBG_TAG, "WRITE_COMPLETE");
722     // Weird: synclient should not close the write part of vconn.
723     // Otherwise some strangeness...
724 
725     /* Start reading */
726     SET_TEST_HANDLER(txn->current_handler, synclient_txn_read_response_handler);
727     txn->read_vio = TSVConnRead(txn->vconn, contp, txn->resp_buffer, INT64_MAX);
728     break;
729 
730   case TS_EVENT_VCONN_EOS:
731     TSDebug(CDBG_TAG, "WRITE_EOS");
732     txn->status = REQUEST_FAILURE;
733     synclient_txn_close(static_cast<ClientTxn *>(TSContDataGet(contp)));
734     TSContDestroy(contp);
735     break;
736 
737   case TS_EVENT_ERROR:
738     TSDebug(CDBG_TAG, "WRITE_ERROR");
739     txn->status = REQUEST_FAILURE;
740     synclient_txn_close(static_cast<ClientTxn *>(TSContDataGet(contp)));
741     TSContDestroy(contp);
742     break;
743 
744   default:
745     TSAssert(!"Invalid event");
746     break;
747   }
748   return TS_EVENT_IMMEDIATE;
749 }
750 
751 static int
752 synclient_txn_connect_handler(TSCont contp, TSEvent event, void *data)
753 {
754   TSAssert((event == TS_EVENT_NET_CONNECT) || (event == TS_EVENT_NET_CONNECT_FAILED));
755 
756   ClientTxn *txn = static_cast<ClientTxn *>(TSContDataGet(contp));
757   TSAssert(txn->magic == MAGIC_ALIVE);
758 
759   if (event == TS_EVENT_NET_CONNECT) {
760     TSDebug(CDBG_TAG, "NET_CONNECT");
761 
762     txn->req_buffer  = TSIOBufferCreate();
763     txn->req_reader  = TSIOBufferReaderAlloc(txn->req_buffer);
764     txn->resp_buffer = TSIOBufferCreate();
765     txn->resp_reader = TSIOBufferReaderAlloc(txn->resp_buffer);
766 
767     txn->response[0]  = '\0';
768     txn->response_len = 0;
769 
770     txn->vconn      = static_cast<TSVConn>(data);
771     txn->local_port = (int)((NetVConnection *)data)->get_local_port();
772 
773     txn->write_vio = nullptr;
774     txn->read_vio  = nullptr;
775 
776     /* start writing */
777     SET_TEST_HANDLER(txn->current_handler, synclient_txn_write_request_handler);
778     synclient_txn_write_request(contp);
779 
780     return TS_EVENT_IMMEDIATE;
781   } else {
782     TSDebug(CDBG_TAG, "NET_CONNECT_FAILED");
783     txn->status = REQUEST_FAILURE;
784     synclient_txn_close(static_cast<ClientTxn *>(TSContDataGet(contp)));
785     TSContDestroy(contp);
786   }
787 
788   return TS_EVENT_IMMEDIATE;
789 }
790 
791 static int
792 synclient_txn_main_handler(TSCont contp, TSEvent event, void *data)
793 {
794   ClientTxn *txn = static_cast<ClientTxn *>(TSContDataGet(contp));
795   TSAssert(txn->magic == MAGIC_ALIVE);
796 
797   TxnHandler handler = txn->current_handler;
798   return (*handler)(contp, event, data);
799 }
800 
801 //////////////////////////////////////////////////////////////////////////////
802 // SOCKET SERVER
803 //////////////////////////////////////////////////////////////////////////////
804 
805 SocketServer *
806 synserver_create(int port, TSCont cont)
807 {
808   if (port != SYNSERVER_DUMMY_PORT) {
809     TSAssert(port > 0);
810     TSAssert(port < INT16_MAX);
811   }
812 
813   SocketServer *s  = static_cast<SocketServer *>(TSmalloc(sizeof(SocketServer)));
814   s->magic         = MAGIC_ALIVE;
815   s->accept_port   = port;
816   s->accept_action = nullptr;
817   s->accept_cont   = cont;
818   TSContDataSet(s->accept_cont, s);
819   return s;
820 }
821 
822 SocketServer *
823 synserver_create(int port)
824 {
825   return synserver_create(port, TSContCreate(synserver_vc_accept, TSMutexCreate()));
826 }
827 
828 static int
829 synserver_start(SocketServer *s)
830 {
831   TSAssert(s->magic == MAGIC_ALIVE);
832   TSAssert(s->accept_action == nullptr);
833 
834   if (s->accept_port != SYNSERVER_DUMMY_PORT) {
835     TSAssert(s->accept_port > 0);
836     TSAssert(s->accept_port < INT16_MAX);
837 
838     s->accept_action = TSNetAccept(s->accept_cont, s->accept_port, AF_INET, 0);
839   }
840 
841   return 1;
842 }
843 
844 static int
845 synserver_stop(SocketServer *s)
846 {
847   TSAssert(s->magic == MAGIC_ALIVE);
848   if (s->accept_action && !TSActionDone(s->accept_action)) {
849     TSActionCancel(s->accept_action);
850     s->accept_action = nullptr;
851     TSDebug(SDBG_TAG, "Had to cancel action");
852   }
853   TSDebug(SDBG_TAG, "stopped");
854   return 1;
855 }
856 
857 static int
858 synserver_delete(SocketServer *s)
859 {
860   if (s != nullptr) {
861     TSAssert(s->magic == MAGIC_ALIVE);
862     synserver_stop(s);
863 
864     if (s->accept_cont) {
865       TSContDestroy(s->accept_cont);
866       s->accept_cont = nullptr;
867       TSDebug(SDBG_TAG, "destroyed accept cont");
868     }
869 
870     s->magic = MAGIC_DEAD;
871     TSfree(s);
872     TSDebug(SDBG_TAG, "deleted server");
873   }
874 
875   return 1;
876 }
877 
878 static int
879 synserver_vc_refuse(TSCont contp, TSEvent event, void *data)
880 {
881   TSAssert((event == TS_EVENT_NET_ACCEPT) || (event == TS_EVENT_NET_ACCEPT_FAILED));
882 
883   SocketServer *s = static_cast<SocketServer *>(TSContDataGet(contp));
884   TSAssert(s->magic == MAGIC_ALIVE);
885 
886   TSDebug(SDBG_TAG, "%s: NET_ACCEPT", __func__);
887 
888   if (event == TS_EVENT_NET_ACCEPT_FAILED) {
889     Warning("Synserver failed to bind to port %d.", ntohs(s->accept_port));
890     ink_release_assert(!"Synserver must be able to bind to a port, check system netstat");
891     TSDebug(SDBG_TAG, "%s: NET_ACCEPT_FAILED", __func__);
892     return TS_EVENT_IMMEDIATE;
893   }
894 
895   TSVConnClose(static_cast<TSVConn>(data));
896   return TS_EVENT_IMMEDIATE;
897 }
898 
899 static int
900 synserver_vc_accept(TSCont contp, TSEvent event, void *data)
901 {
902   TSAssert((event == TS_EVENT_NET_ACCEPT) || (event == TS_EVENT_NET_ACCEPT_FAILED));
903 
904   SocketServer *s = static_cast<SocketServer *>(TSContDataGet(contp));
905   TSAssert(s->magic == MAGIC_ALIVE);
906 
907   if (event == TS_EVENT_NET_ACCEPT_FAILED) {
908     Warning("Synserver failed to bind to port %d.", ntohs(s->accept_port));
909     ink_release_assert(!"Synserver must be able to bind to a port, check system netstat");
910     TSDebug(SDBG_TAG, "%s: NET_ACCEPT_FAILED", __func__);
911     return TS_EVENT_IMMEDIATE;
912   }
913 
914   TSDebug(SDBG_TAG, "%s: NET_ACCEPT", __func__);
915 
916   /* Create a new transaction */
917   ServerTxn *txn = static_cast<ServerTxn *>(TSmalloc(sizeof(ServerTxn)));
918   txn->magic     = MAGIC_ALIVE;
919 
920   SET_TEST_HANDLER(txn->current_handler, synserver_txn_read_request_handler);
921 
922   TSCont txn_cont = TSContCreate(synserver_txn_main_handler, TSMutexCreate());
923   TSContDataSet(txn_cont, txn);
924 
925   txn->req_buffer = TSIOBufferCreate();
926   txn->req_reader = TSIOBufferReaderAlloc(txn->req_buffer);
927 
928   txn->resp_buffer = TSIOBufferCreate();
929   txn->resp_reader = TSIOBufferReaderAlloc(txn->resp_buffer);
930 
931   txn->request[0]  = '\0';
932   txn->request_len = 0;
933 
934   txn->vconn = static_cast<TSVConn>(data);
935 
936   txn->write_vio = nullptr;
937 
938   /* start reading */
939   txn->read_vio = TSVConnRead(txn->vconn, txn_cont, txn->req_buffer, INT64_MAX);
940 
941   return TS_EVENT_IMMEDIATE;
942 }
943 
944 static int
945 synserver_txn_close(TSCont contp)
946 {
947   ServerTxn *txn = static_cast<ServerTxn *>(TSContDataGet(contp));
948   TSAssert(txn->magic == MAGIC_ALIVE);
949 
950   if (txn->vconn != nullptr) {
951     TSVConnClose(txn->vconn);
952   }
953   if (txn->req_buffer) {
954     TSIOBufferDestroy(txn->req_buffer);
955   }
956   if (txn->resp_buffer) {
957     TSIOBufferDestroy(txn->resp_buffer);
958   }
959 
960   txn->magic = MAGIC_DEAD;
961   TSfree(txn);
962   TSContDestroy(contp);
963 
964   TSDebug(SDBG_TAG, "Server Txn destroyed");
965   return TS_EVENT_IMMEDIATE;
966 }
967 
968 static int
969 synserver_txn_write_response(TSCont contp)
970 {
971   ServerTxn *txn = static_cast<ServerTxn *>(TSContDataGet(contp));
972   TSAssert(txn->magic == MAGIC_ALIVE);
973 
974   SET_TEST_HANDLER(txn->current_handler, synserver_txn_write_response_handler);
975 
976   TSIOBufferBlock block;
977   char *ptr_block;
978   int64_t len, ndone, ntodo, towrite, avail;
979   char *response;
980 
981   response = generate_response(txn->request);
982   len      = strlen(response);
983 
984   ndone = 0;
985   ntodo = len;
986   while (ntodo > 0) {
987     block     = TSIOBufferStart(txn->resp_buffer);
988     ptr_block = TSIOBufferBlockWriteStart(block, &avail);
989     towrite   = std::min(ntodo, avail);
990     memcpy(ptr_block, response + ndone, towrite);
991     TSIOBufferProduce(txn->resp_buffer, towrite);
992     ntodo -= towrite;
993     ndone += towrite;
994   }
995 
996   /* Start writing the response */
997   TSDebug(SDBG_TAG, "Writing response: |%s| (%" PRId64 ") bytes)", response, len);
998   txn->write_vio = TSVConnWrite(txn->vconn, contp, txn->resp_reader, len);
999 
1000   /* Now that response is in IOBuffer, free up response */
1001   TSfree(response);
1002 
1003   return TS_EVENT_IMMEDIATE;
1004 }
1005 
1006 static int
1007 synserver_txn_write_response_handler(TSCont contp, TSEvent event, void * /* data ATS_UNUSED */)
1008 {
1009   ServerTxn *txn = static_cast<ServerTxn *>(TSContDataGet(contp));
1010   TSAssert(txn->magic == MAGIC_ALIVE);
1011 
1012   switch (event) {
1013   case TS_EVENT_VCONN_WRITE_READY:
1014     TSDebug(SDBG_TAG, "WRITE_READY");
1015     TSVIOReenable(txn->write_vio);
1016     break;
1017 
1018   case TS_EVENT_VCONN_WRITE_COMPLETE:
1019     TSDebug(SDBG_TAG, "WRITE_COMPLETE");
1020     TSVConnShutdown(txn->vconn, 0, 1);
1021     return synserver_txn_close(contp);
1022     break;
1023 
1024   case TS_EVENT_VCONN_EOS:
1025     TSDebug(SDBG_TAG, "WRITE_EOS");
1026     return synserver_txn_close(contp);
1027     break;
1028 
1029   case TS_EVENT_ERROR:
1030     TSDebug(SDBG_TAG, "WRITE_ERROR");
1031     return synserver_txn_close(contp);
1032     break;
1033 
1034   default:
1035     TSAssert(!"Invalid event");
1036     break;
1037   }
1038   return TS_EVENT_IMMEDIATE;
1039 }
1040 
1041 static int
1042 synserver_txn_read_request(TSCont contp)
1043 {
1044   ServerTxn *txn = static_cast<ServerTxn *>(TSContDataGet(contp));
1045   TSAssert(txn->magic == MAGIC_ALIVE);
1046 
1047   int end;
1048   TSIOBufferBlock block = TSIOBufferReaderStart(txn->req_reader);
1049 
1050   while (block != nullptr) {
1051     int64_t blocklen;
1052     const char *blockptr = TSIOBufferBlockReadStart(block, txn->req_reader, &blocklen);
1053 
1054     if (txn->request_len + blocklen <= REQUEST_MAX_SIZE) {
1055       memcpy((txn->request + txn->request_len), blockptr, blocklen);
1056       txn->request_len += blocklen;
1057     } else {
1058       TSError("Error: Request length %" PRId64 " > request buffer size %d", txn->request_len + blocklen, REQUEST_MAX_SIZE);
1059     }
1060 
1061     block = TSIOBufferBlockNext(block);
1062   }
1063 
1064   txn->request[txn->request_len] = '\0';
1065   TSDebug(SDBG_TAG, "Request = |%s|, req len = %d", txn->request, txn->request_len);
1066 
1067   end = (strstr(txn->request, HTTP_REQUEST_END) != nullptr);
1068   TSDebug(SDBG_TAG, "End of request = %d", end);
1069 
1070   return end;
1071 }
1072 
1073 static int
1074 synserver_txn_read_request_handler(TSCont contp, TSEvent event, void * /* data ATS_UNUSED */)
1075 {
1076   ServerTxn *txn = static_cast<ServerTxn *>(TSContDataGet(contp));
1077   TSAssert(txn->magic == MAGIC_ALIVE);
1078 
1079   int64_t avail;
1080   int end_of_request;
1081 
1082   switch (event) {
1083   case TS_EVENT_VCONN_READ_READY:
1084   case TS_EVENT_VCONN_READ_COMPLETE:
1085     TSDebug(SDBG_TAG, (event == TS_EVENT_VCONN_READ_READY) ? "READ_READY" : "READ_COMPLETE");
1086     avail = TSIOBufferReaderAvail(txn->req_reader);
1087     TSDebug(SDBG_TAG, "%" PRId64 " bytes available in buffer", avail);
1088 
1089     if (avail > 0) {
1090       end_of_request = synserver_txn_read_request(contp);
1091       TSIOBufferReaderConsume(txn->req_reader, avail);
1092 
1093       if (end_of_request) {
1094         TSVConnShutdown(txn->vconn, 1, 0);
1095         return synserver_txn_write_response(contp);
1096       }
1097     }
1098 
1099     TSVIOReenable(txn->read_vio);
1100     break;
1101 
1102   case TS_EVENT_VCONN_EOS:
1103     TSDebug(SDBG_TAG, "READ_EOS");
1104     return synserver_txn_close(contp);
1105     break;
1106 
1107   case TS_EVENT_ERROR:
1108     TSDebug(SDBG_TAG, "READ_ERROR");
1109     return synserver_txn_close(contp);
1110     break;
1111 
1112   default:
1113     TSAssert(!"Invalid event");
1114     break;
1115   }
1116   return TS_EVENT_IMMEDIATE;
1117 }
1118 
1119 static int
1120 synserver_txn_main_handler(TSCont contp, TSEvent event, void *data)
1121 {
1122   ServerTxn *txn = static_cast<ServerTxn *>(TSContDataGet(contp));
1123   TSAssert(txn->magic == MAGIC_ALIVE);
1124 
1125   TxnHandler handler = txn->current_handler;
1126   return (*handler)(contp, event, data);
1127 }
1128 
1129 // End of the previous #include "InkAPITestTool.cc"
1130 
1131 #define TC_PASS 1
1132 #define TC_FAIL 0
1133 
1134 #define UTDBG_TAG "sdk_ut"
1135 
1136 // Since there's no way to unregister global hooks, tests that register a hook
1137 // have to co-operate once they are complete by re-enabling and transactions
1138 // and getting out of the way.
1139 #define CHECK_SPURIOUS_EVENT(cont, event, edata)                     \
1140   if (TSContDataGet(cont) == NULL) {                                 \
1141     switch (event) {                                                 \
1142     case TS_EVENT_IMMEDIATE:                                         \
1143     case TS_EVENT_TIMEOUT:                                           \
1144       return TS_EVENT_NONE;                                          \
1145     case TS_EVENT_HTTP_SELECT_ALT:                                   \
1146       return TS_EVENT_NONE;                                          \
1147     case TS_EVENT_HTTP_READ_REQUEST_HDR:                             \
1148     case TS_EVENT_HTTP_OS_DNS:                                       \
1149     case TS_EVENT_HTTP_SEND_REQUEST_HDR:                             \
1150     case TS_EVENT_HTTP_READ_CACHE_HDR:                               \
1151     case TS_EVENT_HTTP_READ_RESPONSE_HDR:                            \
1152     case TS_EVENT_HTTP_SEND_RESPONSE_HDR:                            \
1153     case TS_EVENT_HTTP_REQUEST_TRANSFORM:                            \
1154     case TS_EVENT_HTTP_RESPONSE_TRANSFORM:                           \
1155     case TS_EVENT_HTTP_TXN_START:                                    \
1156     case TS_EVENT_HTTP_TXN_CLOSE:                                    \
1157     case TS_EVENT_HTTP_SSN_START:                                    \
1158     case TS_EVENT_HTTP_SSN_CLOSE:                                    \
1159     case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:                        \
1160     case TS_EVENT_HTTP_PRE_REMAP:                                    \
1161     case TS_EVENT_HTTP_POST_REMAP:                                   \
1162       TSHttpTxnReenable((TSHttpTxn)(edata), TS_EVENT_HTTP_CONTINUE); \
1163       return TS_EVENT_NONE;                                          \
1164     default:                                                         \
1165       break;                                                         \
1166     }                                                                \
1167   }
1168 
1169 /******************************************************************************/
1170 
1171 /* Use SDK_RPRINT to report failure or success for each test case */
1172 int
1173 SDK_RPRINT(RegressionTest *t, const char *api_name, const char *testcase_name, int status, const char *err_details_format, ...)
1174 {
1175   int l;
1176   char buffer[8192];
1177   char format2[8192];
1178   snprintf(format2, sizeof(format2), "[%s] %s : [%s] <<%s>> { %s }\n", t->name, api_name, testcase_name,
1179            status == TC_PASS ? "PASS" : "FAIL", err_details_format);
1180   va_list ap;
1181   va_start(ap, err_details_format);
1182   l = ink_bvsprintf(buffer, format2, ap);
1183   va_end(ap);
1184   fputs(buffer, stderr);
1185   return (l);
1186 }
1187 
1188 /*
1189   REGRESSION_TEST(SDK_<test_name>)(RegressionTest *t, int atype, int *pstatus)
1190 
1191   RegressionTest *test is a pointer on object that will run the test.
1192    Do not modify.
1193 
1194   int atype is one of:
1195    REGRESSION_TEST_NONE
1196    REGRESSION_TEST_QUICK
1197    REGRESSION_TEST_NIGHTLY
1198    REGRESSION_TEST_EXTENDED
1199 
1200   int *pstatus should be set to one of:
1201    REGRESSION_TEST_PASSED
1202    REGRESSION_TEST_INPROGRESS
1203    REGRESSION_TEST_FAILED
1204    REGRESSION_TEST_NOT_RUN
1205   Note: pstatus is polled and can be used for asynchronous tests.
1206 
1207 */
1208 
1209 /* Misc */
1210 ////////////////////////////////////////////////
1211 //       SDK_API_TSTrafficServerVersionGet
1212 //
1213 // Unit Test for API: TSTrafficServerVersionGet
1214 ////////////////////////////////////////////////
1215 REGRESSION_TEST(SDK_API_TSTrafficServerVersionGet)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
1216 {
1217   *pstatus = REGRESSION_TEST_INPROGRESS;
1218 
1219   /* Assume the UT runs on TS5.0 and higher */
1220   const char *ts_version = TSTrafficServerVersionGet();
1221   if (!ts_version) {
1222     SDK_RPRINT(test, "TSTrafficServerVersionGet", "TestCase1", TC_FAIL, "can't get traffic server version");
1223     *pstatus = REGRESSION_TEST_FAILED;
1224     return;
1225   }
1226 
1227   int major_ts_version = 0;
1228   int minor_ts_version = 0;
1229   int patch_ts_version = 0;
1230   // coverity[secure_coding]
1231   if (sscanf(ts_version, "%d.%d.%d", &major_ts_version, &minor_ts_version, &patch_ts_version) != 3) {
1232     SDK_RPRINT(test, "TSTrafficServerVersionGet", "TestCase2", TC_FAIL, "traffic server version format is incorrect");
1233     *pstatus = REGRESSION_TEST_FAILED;
1234     return;
1235   }
1236 
1237   if (major_ts_version < 2) {
1238     SDK_RPRINT(test, "TSTrafficServerVersionGet", "TestCase3", TC_FAIL, "traffic server major version is incorrect");
1239     *pstatus = REGRESSION_TEST_FAILED;
1240     return;
1241   }
1242 
1243   SDK_RPRINT(test, "TSTrafficServerVersionGet", "TestCase1", TC_PASS, "ok");
1244   *pstatus = REGRESSION_TEST_PASSED;
1245   return;
1246 }
1247 
1248 ////////////////////////////////////////////////
1249 //       SDK_API_TSPluginDirGet
1250 //
1251 // Unit Test for API: TSPluginDirGet
1252 //                    TSInstallDirGet
1253 //                    TSRuntimeDirGet
1254 ////////////////////////////////////////////////
1255 REGRESSION_TEST(SDK_API_TSPluginDirGet)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
1256 {
1257   *pstatus = REGRESSION_TEST_INPROGRESS;
1258 
1259   const char *plugin_dir  = TSPluginDirGet();
1260   const char *install_dir = TSInstallDirGet();
1261   const char *runtime_dir = TSRuntimeDirGet();
1262 
1263   if (!plugin_dir) {
1264     SDK_RPRINT(test, "TSPluginDirGet", "TestCase1", TC_FAIL, "can't get plugin dir");
1265     *pstatus = REGRESSION_TEST_FAILED;
1266     return;
1267   }
1268 
1269   if (!install_dir) {
1270     SDK_RPRINT(test, "TSInstallDirGet", "TestCase1", TC_FAIL, "can't get installation dir");
1271     *pstatus = REGRESSION_TEST_FAILED;
1272     return;
1273   }
1274 
1275   if (!runtime_dir) {
1276     SDK_RPRINT(test, "TSRuntimeDirGet", "TestCase1", TC_FAIL, "can't get runtime dir");
1277     *pstatus = REGRESSION_TEST_FAILED;
1278     return;
1279   }
1280 
1281   if (strstr(plugin_dir, TS_BUILD_LIBEXECDIR) == nullptr) {
1282     SDK_RPRINT(test, "TSPluginDirGet", "TestCase2", TC_FAIL, "plugin dir(%s) is incorrect, expected (%s) in path.", plugin_dir,
1283                TS_BUILD_LIBEXECDIR);
1284     *pstatus = REGRESSION_TEST_FAILED;
1285     return;
1286   }
1287 
1288   if (strstr(plugin_dir, install_dir) == nullptr) {
1289     SDK_RPRINT(test, "TSInstallDirGet", "TestCase2", TC_FAIL, "install dir is incorrect");
1290     *pstatus = REGRESSION_TEST_FAILED;
1291     return;
1292   }
1293 
1294   if (strstr(runtime_dir, TS_BUILD_RUNTIMEDIR) == nullptr) {
1295     SDK_RPRINT(test, "TSRuntimeDirGet", "TestCase2", TC_FAIL, "runtime dir is incorrect");
1296     *pstatus = REGRESSION_TEST_FAILED;
1297     return;
1298   }
1299 
1300   SDK_RPRINT(test, "TSPluginDirGet", "TestCase1", TC_PASS, "ok");
1301   SDK_RPRINT(test, "TSInstallDirGet", "TestCase1", TC_PASS, "ok");
1302   SDK_RPRINT(test, "TSRuntimeDirGet", "TestCase1", TC_PASS, "ok");
1303   *pstatus = REGRESSION_TEST_PASSED;
1304   return;
1305 }
1306 
1307 /* TSConfig */
1308 ////////////////////////////////////////////////
1309 //       SDK_API_TSConfig
1310 //
1311 // Unit Test for API: TSConfigSet
1312 //                    TSConfigGet
1313 //                    TSConfigRelease
1314 //                    TSConfigDataGet
1315 ////////////////////////////////////////////////
1316 static int my_config_id = 0;
1317 struct ConfigData {
1318   const char *a;
1319   const char *b;
1320 };
1321 
1322 REGRESSION_TEST(SDK_API_TSConfig)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
1323 {
1324   *pstatus           = REGRESSION_TEST_INPROGRESS;
1325   ConfigData *config = new ConfigData;
1326   config->a          = "unit";
1327   config->b          = "test";
1328 
1329   my_config_id = TSConfigSet(my_config_id, config, [](void *cfg) { delete static_cast<ConfigData *>(cfg); });
1330 
1331   TSConfig test_config = nullptr;
1332   test_config          = TSConfigGet(my_config_id);
1333 
1334   if (!test_config) {
1335     SDK_RPRINT(test, "TSConfigSet", "TestCase1", TC_FAIL, "can't correctly set global config structure");
1336     SDK_RPRINT(test, "TSConfigGet", "TestCase1", TC_FAIL, "can't correctly get global config structure");
1337     TSConfigRelease(my_config_id, reinterpret_cast<TSConfig>(config));
1338     *pstatus = REGRESSION_TEST_FAILED;
1339     return;
1340   }
1341 
1342   if (TSConfigDataGet(test_config) != config) {
1343     SDK_RPRINT(test, "TSConfigDataGet", "TestCase1", TC_FAIL, "failed to get config data");
1344     TSConfigRelease(my_config_id, reinterpret_cast<TSConfig>(config));
1345     *pstatus = REGRESSION_TEST_FAILED;
1346     return;
1347   }
1348 
1349   SDK_RPRINT(test, "TSConfigGet", "TestCase1", TC_PASS, "ok");
1350   SDK_RPRINT(test, "TSConfigSet", "TestCase1", TC_PASS, "ok");
1351   SDK_RPRINT(test, "TSConfigDataGet", "TestCase1", TC_PASS, "ok");
1352 
1353   TSConfigRelease(my_config_id, reinterpret_cast<TSConfig>(config));
1354   *pstatus = REGRESSION_TEST_PASSED;
1355   return;
1356 }
1357 
1358 /* TSNetVConn */
1359 //////////////////////////////////////////////
1360 //       SDK_API_TSNetVConn
1361 //
1362 // Unit Test for API: TSNetVConnRemoteIPGet
1363 //                    TSNetVConnRemotePortGet
1364 //                    TSNetAccept
1365 //                    TSNetConnect
1366 //////////////////////////////////////////////
1367 
1368 struct SDK_NetVConn_Params {
1369   SDK_NetVConn_Params(const char *_a, RegressionTest *_t, int *_p)
1370     : buffer(nullptr), api(_a), port(0), test(_t), pstatus(_p), vc(nullptr)
1371   {
1372     this->status.client = this->status.server = REGRESSION_TEST_INPROGRESS;
1373   }
1374 
1375   ~SDK_NetVConn_Params()
1376   {
1377     if (this->buffer) {
1378       TSIOBufferDestroy(this->buffer);
1379     }
1380     if (this->vc) {
1381       TSVConnClose(this->vc);
1382     }
1383   }
1384 
1385   TSIOBuffer buffer;
1386   const char *api;
1387   unsigned short port;
1388   RegressionTest *test;
1389   int *pstatus;
1390   TSVConn vc;
1391   struct {
1392     int client;
1393     int server;
1394   } status;
1395 };
1396 
1397 int
1398 server_handler(TSCont contp, TSEvent event, void *data)
1399 {
1400   SDK_NetVConn_Params *params = static_cast<SDK_NetVConn_Params *>(TSContDataGet(contp));
1401 
1402   if (event == TS_EVENT_NET_ACCEPT) {
1403     // Kick off a read so that we can receive an EOS event.
1404     SDK_RPRINT(params->test, params->api, "ServerEvent NET_ACCEPT", TC_PASS, "ok");
1405     params->buffer = TSIOBufferCreate();
1406     params->vc     = static_cast<TSVConn>(data);
1407     TSVConnRead(static_cast<TSVConn>(data), contp, params->buffer, 100);
1408   } else if (event == TS_EVENT_VCONN_EOS) {
1409     // The server end of the test passes if it receives an EOF event. This means that it must have
1410     // connected to the endpoint. Since this always happens *after* the accept, we know that it is
1411     // safe to delete the params.
1412     TSContDestroy(contp);
1413 
1414     SDK_RPRINT(params->test, params->api, "ServerEvent EOS", TC_PASS, "ok");
1415     *params->pstatus = REGRESSION_TEST_PASSED;
1416     delete params;
1417   } else if (event == TS_EVENT_VCONN_READ_READY) {
1418     SDK_RPRINT(params->test, params->api, "ServerEvent READ_READY", TC_PASS, "ok");
1419   } else {
1420     SDK_RPRINT(params->test, params->api, "ServerEvent", TC_FAIL, "received unexpected event %d", event);
1421     *params->pstatus = REGRESSION_TEST_FAILED;
1422     delete params;
1423   }
1424 
1425   return 1;
1426 }
1427 
1428 int
1429 client_handler(TSCont contp, TSEvent event, void *data)
1430 {
1431   SDK_NetVConn_Params *params = static_cast<SDK_NetVConn_Params *>(TSContDataGet(contp));
1432 
1433   if (event == TS_EVENT_NET_CONNECT_FAILED) {
1434     SDK_RPRINT(params->test, params->api, "ClientConnect", TC_FAIL, "can't connect to server");
1435 
1436     *params->pstatus = REGRESSION_TEST_FAILED;
1437 
1438     // no need to continue, return
1439     // Fix me: how to deal with server side cont?
1440     TSContDestroy(contp);
1441     return 1;
1442   } else if (TS_EVENT_NET_CONNECT == event) {
1443     sockaddr const *addr       = TSNetVConnRemoteAddrGet(static_cast<TSVConn>(data));
1444     uint16_t input_server_port = ats_ip_port_host_order(addr);
1445 
1446     // If DEFER_ACCEPT is enabled in the OS then the user space accept() doesn't
1447     // happen until data arrives on the socket. Because we're just testing the accept()
1448     // we write a small amount of ignored data to make sure this gets triggered.
1449     UnixNetVConnection *vc = static_cast<UnixNetVConnection *>(data);
1450     ink_release_assert(::write(vc->con.fd, "Bob's your uncle", 16) != 0);
1451 
1452     sleep(1); // XXX this sleep ensures the server end gets the accept event.
1453 
1454     if (ats_is_ip_loopback(addr)) {
1455       SDK_RPRINT(params->test, params->api, "TSNetVConnRemoteIPGet", TC_PASS, "ok");
1456     } else {
1457       ip_text_buffer s, ipb;
1458       IpEndpoint loopback;
1459       ats_ip4_set(&loopback, htonl(INADDR_LOOPBACK));
1460       SDK_RPRINT(params->test, params->api, "TSNetVConnRemoteIPGet", TC_FAIL, "server ip [%s] is incorrect - expected [%s]",
1461                  ats_ip_ntop(addr, s, sizeof s), ats_ip_ntop(&loopback.sa, ipb, sizeof ipb));
1462 
1463       TSContDestroy(contp);
1464       // Fix me: how to deal with server side cont?
1465       *params->pstatus = REGRESSION_TEST_FAILED;
1466       return 1;
1467     }
1468 
1469     if (input_server_port == params->port) {
1470       SDK_RPRINT(params->test, params->api, "TSNetVConnRemotePortGet", TC_PASS, "ok");
1471     } else {
1472       SDK_RPRINT(params->test, params->api, "TSNetVConnRemotePortGet", TC_FAIL, "server port [%d] is incorrect -- expected [%d]",
1473                  input_server_port, params->port);
1474 
1475       TSContDestroy(contp);
1476       // Fix me: how to deal with server side cont?
1477       *params->pstatus = REGRESSION_TEST_FAILED;
1478       return 1;
1479     }
1480 
1481     SDK_RPRINT(params->test, params->api, "TSNetConnect", TC_PASS, "ok");
1482 
1483     // XXX We really ought to do a write/read exchange with the server. The sleep above works around this.
1484 
1485     // Looks good from the client end. Next we disconnect so that the server end can set the final test status.
1486     TSVConnClose(static_cast<TSVConn>(data));
1487   }
1488 
1489   TSContDestroy(contp);
1490 
1491   return 1;
1492 }
1493 
1494 REGRESSION_TEST(SDK_API_TSNetVConn)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
1495 {
1496   *pstatus = REGRESSION_TEST_INPROGRESS;
1497 
1498   SDK_NetVConn_Params *params = new SDK_NetVConn_Params("TSNetAccept", test, pstatus);
1499 
1500   params->port = 12345;
1501 
1502   TSCont server_cont = TSContCreate(server_handler, TSMutexCreate());
1503   TSCont client_cont = TSContCreate(client_handler, TSMutexCreate());
1504 
1505   TSContDataSet(server_cont, params);
1506   TSContDataSet(client_cont, params);
1507 
1508   TSNetAccept(server_cont, params->port, -1, 0);
1509 
1510   IpEndpoint addr;
1511   ats_ip4_set(&addr, htonl(INADDR_LOOPBACK), htons(params->port));
1512   TSNetConnect(client_cont, &addr.sa);
1513 }
1514 
1515 REGRESSION_TEST(SDK_API_TSPortDescriptor)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
1516 {
1517   *pstatus = REGRESSION_TEST_INPROGRESS;
1518 
1519   TSPortDescriptor port;
1520   char desc[64];
1521   SDK_NetVConn_Params *params = new SDK_NetVConn_Params("TSPortDescriptorAccept", test, pstatus);
1522   TSCont server_cont          = TSContCreate(server_handler, TSMutexCreate());
1523   TSCont client_cont          = TSContCreate(client_handler, TSMutexCreate());
1524 
1525   params->port = 54321;
1526 
1527   TSContDataSet(server_cont, params);
1528   TSContDataSet(client_cont, params);
1529 
1530   port = TSPortDescriptorParse(nullptr);
1531   if (port) {
1532     SDK_RPRINT(test, "TSPortDescriptorParse", "NULL port descriptor", TC_FAIL, "TSPortDescriptorParse(NULL) returned %s", port);
1533     *pstatus = REGRESSION_TEST_FAILED;
1534     return;
1535   }
1536 
1537   snprintf(desc, sizeof(desc), "%u", params->port);
1538   port = TSPortDescriptorParse(desc);
1539 
1540   if (TSPortDescriptorAccept(port, server_cont) == TS_ERROR) {
1541     SDK_RPRINT(test, "TSPortDescriptorParse", "Basic port descriptor", TC_FAIL, "TSPortDescriptorParse(%s) returned TS_ERROR",
1542                desc);
1543     *pstatus = REGRESSION_TEST_FAILED;
1544     return;
1545   }
1546 
1547   IpEndpoint addr;
1548   ats_ip4_set(&addr, htonl(INADDR_LOOPBACK), htons(params->port));
1549   TSNetConnect(client_cont, &addr.sa);
1550 }
1551 
1552 /* TSCache, TSVConn, TSVIO */
1553 //////////////////////////////////////////////
1554 //       SDK_API_TSCache
1555 //
1556 // Unit Test for API: TSCacheReady
1557 //                    TSCacheWrite
1558 //                    TSCacheRead
1559 //                    TSCacheKeyCreate
1560 //                    TSCacheKeyDigestSet
1561 //                    TSVConnCacheObjectSizeGet
1562 //                    TSVConnClose
1563 //                    TSVConnClosedGet
1564 //                    TSVConnRead
1565 //                    TSVConnReadVIOGet
1566 //                    TSVConnWrite
1567 //                    TSVConnWriteVIOGet
1568 //                    TSVIOBufferGet
1569 //                    TSVIOContGet
1570 //                    TSVIOMutexGet
1571 //                    TSVIONBytesGet
1572 //                    TSVIONBytesSet
1573 //                    TSVIONDoneGet
1574 //                    TSVIONDoneSet
1575 //                    TSVIONTodoGet
1576 //                    TSVIOReaderGet
1577 //                    TSVIOReenable
1578 //                    TSVIOVConnGet
1579 //////////////////////////////////////////////
1580 
1581 // TSVConnAbort can't be tested
1582 // Fix me: test TSVConnShutdown, TSCacheKeyDataTypeSet,
1583 //         TSCacheKeyHostNameSet, TSCacheKeyPinnedSet
1584 
1585 // Logic of the test:
1586 //  - write OBJECT_SIZE bytes in the cache in 3 shots
1587 //    (OBJECT_SIZE/2, then OBJECT_SIZE-100 and finally OBJECT_SIZE)
1588 //  - read object from the cache
1589 //  - remove it from the cache
1590 //  - try to read it (should fail)
1591 
1592 #define OBJECT_SIZE 100000 // size of the object we'll write/read/remove in cache
1593 
1594 RegressionTest *SDK_Cache_test;
1595 int *SDK_Cache_pstatus;
1596 static char content[OBJECT_SIZE];
1597 static int read_counter = 0;
1598 
1599 typedef struct {
1600   TSIOBuffer bufp;
1601   TSIOBuffer out_bufp;
1602   TSIOBufferReader readerp;
1603   TSIOBufferReader out_readerp;
1604 
1605   TSVConn write_vconnp;
1606   TSVConn read_vconnp;
1607   TSVIO read_vio;
1608   TSVIO write_vio;
1609 
1610   TSCacheKey key;
1611 } CacheVConnStruct;
1612 
1613 int
1614 cache_handler(TSCont contp, TSEvent event, void *data)
1615 {
1616   Debug("sdk_ut_cache_write", "Event %d data %p", event, data);
1617 
1618   CacheVConnStruct *cache_vconn = static_cast<CacheVConnStruct *>(TSContDataGet(contp));
1619 
1620   TSIOBufferBlock blockp;
1621   char *ptr_block;
1622   int64_t ntodo, ndone, nbytes, towrite, avail, content_length;
1623 
1624   switch (event) {
1625   case TS_EVENT_CACHE_OPEN_WRITE:
1626     Debug(UTDBG_TAG "_cache_event", "TS_EVENT_CACHE_OPEN_WRITE %d %p", event, data);
1627     SDK_RPRINT(SDK_Cache_test, "TSCacheWrite", "TestCase1", TC_PASS, "ok");
1628 
1629     // data is write_vc
1630     cache_vconn->write_vconnp = static_cast<TSVConn>(data);
1631 
1632     // Create buffers/readers to write and read data into the cache
1633     cache_vconn->bufp        = TSIOBufferCreate();
1634     cache_vconn->readerp     = TSIOBufferReaderAlloc(cache_vconn->bufp);
1635     cache_vconn->out_bufp    = TSIOBufferCreate();
1636     cache_vconn->out_readerp = TSIOBufferReaderAlloc(cache_vconn->out_bufp);
1637 
1638     // Write content into upstream IOBuffer
1639     ntodo = OBJECT_SIZE;
1640     ndone = 0;
1641     while (ntodo > 0) {
1642       blockp    = TSIOBufferStart(cache_vconn->bufp);
1643       ptr_block = TSIOBufferBlockWriteStart(blockp, &avail);
1644       towrite   = ((ntodo < avail) ? ntodo : avail);
1645       memcpy(ptr_block, content + ndone, towrite);
1646       TSIOBufferProduce(cache_vconn->bufp, towrite);
1647       ntodo -= towrite;
1648       ndone += towrite;
1649     }
1650 
1651     // first write half of the data. To test TSVIOReenable
1652     cache_vconn->write_vio = TSVConnWrite(static_cast<TSVConn>(data), contp, cache_vconn->readerp, OBJECT_SIZE / 2);
1653     return 1;
1654 
1655   case TS_EVENT_CACHE_OPEN_WRITE_FAILED:
1656     Debug(UTDBG_TAG "_cache_event", "TS_EVENT_CACHE_OPEN_WRITE_FAILED %d %p", event, data);
1657     SDK_RPRINT(SDK_Cache_test, "TSCacheWrite", "TestCase1", TC_FAIL, "can't open cache vc, edtata = %p", data);
1658     TSReleaseAssert(!"cache");
1659 
1660     // no need to continue, return
1661     *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1662     return 1;
1663 
1664   case TS_EVENT_CACHE_OPEN_READ:
1665     Debug(UTDBG_TAG "_cache_event", "TS_EVENT_CACHE_OPEN_READ %d %p", event, data);
1666     if (read_counter == 2) {
1667       SDK_RPRINT(SDK_Cache_test, "TSCacheRead", "TestCase2", TC_FAIL, "shouldn't open cache vc");
1668 
1669       // no need to continue, return
1670       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1671       return 1;
1672     }
1673 
1674     SDK_RPRINT(SDK_Cache_test, "TSCacheRead", "TestCase1", TC_PASS, "ok");
1675 
1676     cache_vconn->read_vconnp = static_cast<TSVConn>(data);
1677     content_length           = TSVConnCacheObjectSizeGet(cache_vconn->read_vconnp);
1678     Debug(UTDBG_TAG "_cache_read", "In cache open read [Content-Length: %" PRId64 "]", content_length);
1679     if (content_length != OBJECT_SIZE) {
1680       SDK_RPRINT(SDK_Cache_test, "TSVConnCacheObjectSizeGet", "TestCase1", TC_FAIL, "cached data size is incorrect");
1681 
1682       // no need to continue, return
1683       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1684       return 1;
1685     } else {
1686       SDK_RPRINT(SDK_Cache_test, "TSVConnCacheObjectSizeGet", "TestCase1", TC_PASS, "ok");
1687       cache_vconn->read_vio = TSVConnRead(static_cast<TSVConn>(data), contp, cache_vconn->out_bufp, content_length);
1688     }
1689     return 1;
1690 
1691   case TS_EVENT_CACHE_OPEN_READ_FAILED:
1692     Debug(UTDBG_TAG "_cache_event", "TS_EVENT_CACHE_OPEN_READ_FAILED %d %p", event, data);
1693     if (read_counter == 1) {
1694       SDK_RPRINT(SDK_Cache_test, "TSCacheRead", "TestCase1", TC_FAIL, "can't open cache vc");
1695 
1696       // no need to continue, return
1697       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1698       return 1;
1699     }
1700     SDK_RPRINT(SDK_Cache_test, "TSCacheRead", "TestCase2", TC_PASS, "ok");
1701 
1702     // ok, all tests passed!
1703     break;
1704 
1705   case TS_EVENT_CACHE_REMOVE:
1706     Debug(UTDBG_TAG "_cache_event", "TS_EVENT_CACHE_REMOVE %d %p", event, data);
1707     SDK_RPRINT(SDK_Cache_test, "TSCacheRemove", "TestCase1", TC_PASS, "ok");
1708 
1709     // read the data which has been removed
1710     read_counter++;
1711     TSCacheRead(contp, cache_vconn->key);
1712     return 1;
1713 
1714   case TS_EVENT_CACHE_REMOVE_FAILED:
1715     Debug(UTDBG_TAG "_cache_event", "TS_EVENT_CACHE_REMOVE_FAILED %d %p", event, data);
1716     SDK_RPRINT(SDK_Cache_test, "TSCacheRemove", "TestCase1", TC_FAIL, "can't remove cached item");
1717 
1718     // no need to continue, return
1719     *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1720     return 1;
1721 
1722   case TS_EVENT_VCONN_WRITE_COMPLETE:
1723     Debug(UTDBG_TAG "_cache_event", "TS_EVENT_VCONN_WRITE_COMPLETE %d %p", event, data);
1724 
1725     // VConn/VIO APIs
1726     nbytes = TSVIONBytesGet(cache_vconn->write_vio);
1727     ndone  = TSVIONDoneGet(cache_vconn->write_vio);
1728     ntodo  = TSVIONTodoGet(cache_vconn->write_vio);
1729     Debug(UTDBG_TAG "_cache_write", "Nbytes=%" PRId64 " Ndone=%" PRId64 " Ntodo=%" PRId64 "", nbytes, ndone, ntodo);
1730 
1731     if (ndone == (OBJECT_SIZE / 2)) {
1732       TSVIONBytesSet(cache_vconn->write_vio, (OBJECT_SIZE - 100));
1733       TSVIOReenable(cache_vconn->write_vio);
1734       Debug(UTDBG_TAG "_cache_write", "Increment write_counter in write_complete [a]");
1735       return 1;
1736     } else if (ndone == (OBJECT_SIZE - 100)) {
1737       TSVIONBytesSet(cache_vconn->write_vio, OBJECT_SIZE);
1738       TSVIOReenable(cache_vconn->write_vio);
1739       Debug(UTDBG_TAG "_cache_write", "Increment write_counter in write_complete [b]");
1740       return 1;
1741     } else if (ndone == OBJECT_SIZE) {
1742       Debug(UTDBG_TAG "_cache_write", "finishing up [c]");
1743 
1744       SDK_RPRINT(SDK_Cache_test, "TSVIOReenable", "TestCase2", TC_PASS, "ok");
1745       SDK_RPRINT(SDK_Cache_test, "TSVIONBytesSet", "TestCase1", TC_PASS, "ok");
1746       SDK_RPRINT(SDK_Cache_test, "TSVConnWrite", "TestCase1", TC_PASS, "ok");
1747     } else {
1748       SDK_RPRINT(SDK_Cache_test, "TSCacheWrite", "TestCase1", TC_FAIL, "Did not write expected # of bytes");
1749       // no need to continue, return
1750       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1751       return 1;
1752     }
1753 
1754     if (static_cast<TSVIO>(data) != cache_vconn->write_vio) {
1755       SDK_RPRINT(SDK_Cache_test, "TSVConnWrite", "TestCase1", TC_FAIL, "write_vio corrupted");
1756       // no need to continue, return
1757       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1758       return 1;
1759     }
1760     Debug(UTDBG_TAG "_cache_write", "finishing up [d]");
1761 
1762     if (TSVIOBufferGet(cache_vconn->write_vio) != cache_vconn->bufp) {
1763       SDK_RPRINT(SDK_Cache_test, "TSVIOBufferGet", "TestCase1", TC_FAIL, "write_vio corrupted");
1764       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1765       return 1;
1766     } else {
1767       SDK_RPRINT(SDK_Cache_test, "TSVIOBufferGet", "TestCase1", TC_PASS, "ok");
1768     }
1769 
1770     if (TSVIOContGet(cache_vconn->write_vio) != contp) {
1771       SDK_RPRINT(SDK_Cache_test, "TSVIOContGet", "TestCase1", TC_FAIL, "write_vio corrupted");
1772       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1773       return 1;
1774     } else {
1775       SDK_RPRINT(SDK_Cache_test, "TSVIOContGet", "TestCase1", TC_PASS, "ok");
1776     }
1777 
1778     Debug(UTDBG_TAG "_cache_write", "finishing up [f]");
1779 
1780     if (TSVIOMutexGet(cache_vconn->write_vio) != TSContMutexGet(contp)) {
1781       SDK_RPRINT(SDK_Cache_test, "TSVIOMutexGet", "TestCase1", TC_FAIL, "write_vio corrupted");
1782       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1783       return 1;
1784     } else {
1785       SDK_RPRINT(SDK_Cache_test, "TSVIOMutexGet", "TestCase1", TC_PASS, "ok");
1786     }
1787 
1788     if (TSVIOVConnGet(cache_vconn->write_vio) != cache_vconn->write_vconnp) {
1789       SDK_RPRINT(SDK_Cache_test, "TSVIOVConnGet", "TestCase1", TC_FAIL, "write_vio corrupted");
1790       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1791       return 1;
1792     } else {
1793       SDK_RPRINT(SDK_Cache_test, "TSVIOVConnGet", "TestCase1", TC_PASS, "ok");
1794     }
1795 
1796     Debug(UTDBG_TAG "_cache_write", "finishing up [g]");
1797 
1798     if (TSVIOReaderGet(cache_vconn->write_vio) != cache_vconn->readerp) {
1799       SDK_RPRINT(SDK_Cache_test, "TSVIOReaderGet", "TestCase1", TC_FAIL, "write_vio corrupted");
1800       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1801       return 1;
1802     } else {
1803       SDK_RPRINT(SDK_Cache_test, "TSVIOReaderGet", "TestCase1", TC_PASS, "ok");
1804     }
1805 
1806     // tests for write is done, close write_vconnp
1807     TSVConnClose(cache_vconn->write_vconnp);
1808     cache_vconn->write_vconnp = nullptr;
1809 
1810     Debug(UTDBG_TAG "_cache_write", "finishing up [h]");
1811 
1812     // start to read data out of cache
1813     read_counter++;
1814     TSCacheRead(contp, cache_vconn->key);
1815     Debug(UTDBG_TAG "_cache_read", "starting read [i]");
1816     return 1;
1817 
1818   case TS_EVENT_VCONN_WRITE_READY:
1819     Debug(UTDBG_TAG "_cache_event", "TS_EVENT_VCONN_WRITE_READY %d %p", event, data);
1820     if (static_cast<TSVIO>(data) != cache_vconn->write_vio) {
1821       SDK_RPRINT(SDK_Cache_test, "TSVConnWrite", "TestCase1", TC_FAIL, "write_vio corrupted");
1822       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1823       return 1;
1824     }
1825 
1826     nbytes = TSVIONBytesGet(cache_vconn->write_vio);
1827     ndone  = TSVIONDoneGet(cache_vconn->write_vio);
1828     ntodo  = TSVIONTodoGet(cache_vconn->write_vio);
1829     Debug(UTDBG_TAG "_cache_write", "Nbytes=%" PRId64 " Ndone=%" PRId64 " Ntodo=%" PRId64 "", nbytes, ndone, ntodo);
1830 
1831     TSVIOReenable(cache_vconn->write_vio);
1832     return 1;
1833 
1834   case TS_EVENT_VCONN_READ_COMPLETE:
1835     Debug(UTDBG_TAG "_cache_event", "TS_EVENT_VCONN_READ_COMPLETE %d %p", event, data);
1836     if (static_cast<TSVIO>(data) != cache_vconn->read_vio) {
1837       SDK_RPRINT(SDK_Cache_test, "TSVConnRead", "TestCase1", TC_FAIL, "read_vio corrupted");
1838 
1839       // no need to continue, return
1840       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1841       return 1;
1842     }
1843 
1844     nbytes = TSVIONBytesGet(cache_vconn->read_vio);
1845     ntodo  = TSVIONTodoGet(cache_vconn->read_vio);
1846     ndone  = TSVIONDoneGet(cache_vconn->read_vio);
1847     Debug(UTDBG_TAG "_cache_read", "Nbytes=%" PRId64 " Ndone=%" PRId64 " Ntodo=%" PRId64 "", nbytes, ndone, ntodo);
1848 
1849     if (nbytes != (ndone + ntodo)) {
1850       SDK_RPRINT(SDK_Cache_test, "TSVIONBytesGet", "TestCase1", TC_FAIL, "read_vio corrupted");
1851       SDK_RPRINT(SDK_Cache_test, "TSVIONTodoGet", "TestCase1", TC_FAIL, "read_vio corrupted");
1852       SDK_RPRINT(SDK_Cache_test, "TSVIONDoneGet", "TestCase1", TC_FAIL, "read_vio corrupted");
1853 
1854       // no need to continue, return
1855       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1856       return 1;
1857     } else {
1858       SDK_RPRINT(SDK_Cache_test, "TSVIONBytesGet", "TestCase1", TC_PASS, "ok");
1859       SDK_RPRINT(SDK_Cache_test, "TSVIONTodoGet", "TestCase1", TC_PASS, "ok");
1860       SDK_RPRINT(SDK_Cache_test, "TSVIONDoneGet", "TestCase1", TC_PASS, "ok");
1861 
1862       TSVIONDoneSet(cache_vconn->read_vio, 0);
1863       if (TSVIONDoneGet(cache_vconn->read_vio) != 0) {
1864         SDK_RPRINT(SDK_Cache_test, "TSVIONDoneSet", "TestCase1", TC_FAIL, "fail to set");
1865 
1866         // no need to continue, return
1867         *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1868         return 1;
1869       } else {
1870         SDK_RPRINT(SDK_Cache_test, "TSVIONDoneSet", "TestCase1", TC_PASS, "ok");
1871       }
1872 
1873       Debug(UTDBG_TAG "_cache_write", "finishing up [i]");
1874 
1875       // now waiting for 100ms to make sure the key is
1876       // written in directory remove the content
1877       TSContScheduleOnPool(contp, 100, TS_THREAD_POOL_NET);
1878     }
1879 
1880     return 1;
1881 
1882   case TS_EVENT_VCONN_READ_READY:
1883     Debug(UTDBG_TAG "_cache_event", "TS_EVENT_VCONN_READ_READY %d %p", event, data);
1884     if (static_cast<TSVIO>(data) != cache_vconn->read_vio) {
1885       SDK_RPRINT(SDK_Cache_test, "TSVConnRead", "TestCase1", TC_FAIL, "read_vio corrupted");
1886 
1887       // no need to continue, return
1888       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1889       return 1;
1890     }
1891 
1892     nbytes = TSVIONBytesGet(cache_vconn->read_vio);
1893     ntodo  = TSVIONTodoGet(cache_vconn->read_vio);
1894     ndone  = TSVIONDoneGet(cache_vconn->read_vio);
1895     Debug(UTDBG_TAG "_cache_read", "Nbytes=%" PRId64 " Ndone=%" PRId64 " Ntodo=%" PRId64 "", nbytes, ndone, ntodo);
1896 
1897     if (nbytes != (ndone + ntodo)) {
1898       SDK_RPRINT(SDK_Cache_test, "TSVIONBytesGet", "TestCase1", TC_FAIL, "read_vio corrupted");
1899       SDK_RPRINT(SDK_Cache_test, "TSVIONTodoGet", "TestCase1", TC_FAIL, "read_vio corrupted");
1900       SDK_RPRINT(SDK_Cache_test, "TSVIONDoneGet", "TestCase1", TC_FAIL, "read_vio corrupted");
1901 
1902       // no need to continue, return
1903       *SDK_Cache_pstatus = REGRESSION_TEST_FAILED;
1904       return 1;
1905     } else {
1906       SDK_RPRINT(SDK_Cache_test, "TSVIONBytesGet", "TestCase1", TC_PASS, "ok");
1907       SDK_RPRINT(SDK_Cache_test, "TSVIONTodoGet", "TestCase1", TC_PASS, "ok");
1908       SDK_RPRINT(SDK_Cache_test, "TSVIONDoneGet", "TestCase1", TC_PASS, "ok");
1909     }
1910 
1911     // Fix for bug INKqa12276: Must consume data from iobuffer
1912     nbytes = TSIOBufferReaderAvail(cache_vconn->out_readerp);
1913     TSIOBufferReaderConsume(cache_vconn->out_readerp, nbytes);
1914     TSDebug(UTDBG_TAG "_cache_read", "Consuming %" PRId64 " bytes from cache read VC", nbytes);
1915 
1916     TSVIOReenable(cache_vconn->read_vio);
1917     Debug(UTDBG_TAG "_cache_read", "finishing up [j]");
1918     return 1;
1919 
1920   case TS_EVENT_TIMEOUT:
1921     Debug(UTDBG_TAG "_cache_event", "TS_EVENT_TIMEOUT %d %p", event, data);
1922     // do remove cached doc
1923     TSCacheRemove(contp, cache_vconn->key);
1924     return 1;
1925 
1926   default:
1927     TSReleaseAssert(!"Test SDK_API_TSCache: unexpected event");
1928   }
1929 
1930   Debug(UTDBG_TAG "_cache_event", "DONE DONE DONE");
1931 
1932   // destroy the data structure
1933   Debug(UTDBG_TAG "_cache_write", "all tests passed [z]");
1934   TSIOBufferDestroy(cache_vconn->bufp);
1935   TSIOBufferDestroy(cache_vconn->out_bufp);
1936   TSCacheKeyDestroy(cache_vconn->key);
1937   TSfree(cache_vconn);
1938   *SDK_Cache_pstatus = REGRESSION_TEST_PASSED;
1939 
1940   return 1;
1941 }
1942 
1943 REGRESSION_TEST(SDK_API_TSCache)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
1944 {
1945   *pstatus          = REGRESSION_TEST_INPROGRESS;
1946   SDK_Cache_test    = test;
1947   SDK_Cache_pstatus = pstatus;
1948   int is_ready      = 0;
1949 
1950   // Check if Cache is ready
1951   TSCacheReady(&is_ready);
1952   if (!is_ready) {
1953     SDK_RPRINT(test, "TSCacheReady", "TestCase1", TC_FAIL, "cache is not ready");
1954 
1955     // no need to continue, return
1956     *pstatus = REGRESSION_TEST_FAILED;
1957     return;
1958   } else {
1959     SDK_RPRINT(test, "TSCacheReady", "TestCase1", TC_PASS, "ok");
1960   }
1961 
1962   // Create CacheKey
1963   char key_name[]    = "key_for_regression_test";
1964   TSCacheKey key     = TSCacheKeyCreate();
1965   TSCacheKey key_cmp = TSCacheKeyCreate();
1966   SDK_RPRINT(test, "TSCacheKeyCreate", "TestCase1", TC_PASS, "ok");
1967   TSCacheKeyDigestSet(key, key_name, strlen(key_name));
1968   TSCacheKeyDigestSet(key_cmp, key_name, strlen(key_name));
1969 
1970   // prepare caching content
1971   // string, null-terminated.
1972   for (int i = 0; i < (OBJECT_SIZE - 1); i++) {
1973     content[i] = 'a';
1974   }
1975   content[OBJECT_SIZE - 1] = '\0';
1976 
1977   // Write data to cache.
1978   TSCont contp                  = TSContCreate(cache_handler, TSMutexCreate());
1979   CacheVConnStruct *cache_vconn = static_cast<CacheVConnStruct *>(TSmalloc(sizeof(CacheVConnStruct)));
1980   cache_vconn->key              = key;
1981   TSContDataSet(contp, cache_vconn);
1982 
1983   TSCacheWrite(contp, key);
1984 }
1985 
1986 /* TSfopen */
1987 
1988 //////////////////////////////////////////////
1989 //       SDK_API_TSfopen
1990 //
1991 // Unit Test for API: TSfopen
1992 //                    TSclose
1993 //                    TSfflush
1994 //                    TSfgets
1995 //                    TSfread
1996 //                    TSfwrite
1997 //////////////////////////////////////////////
1998 #define PFX "plugin.config"
1999 
2000 // Note that for each test, if it fails, we set the error status and return.
2001 REGRESSION_TEST(SDK_API_TSfopen)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2002 {
2003   *pstatus = REGRESSION_TEST_INPROGRESS;
2004 
2005   char write_file_name[PATH_NAME_MAX];
2006 
2007   TSFile source_read_file; // existing file
2008   TSFile write_file;       // to be created
2009   TSFile cmp_read_file;    // read & compare
2010 
2011   char input_buffer[BUFSIZ];
2012   char cmp_buffer[BUFSIZ];
2013   struct stat stat_buffer_pre, stat_buffer_post, stat_buffer_input;
2014   char *ret_val;
2015   int read = 0, wrote = 0;
2016   int64_t read_amount    = 0;
2017   char INPUT_TEXT_FILE[] = "plugin.config";
2018   char input_file_full_path[BUFSIZ];
2019 
2020   // Set full path to file at run time.
2021   // TODO: This can never fail since we are
2022   //       returning the char[]
2023   //       Better check the dir itself.
2024   //
2025   if (TSInstallDirGet() == nullptr) {
2026     *pstatus = REGRESSION_TEST_FAILED;
2027     return;
2028   }
2029   // Add "etc/trafficserver" to point to config directory
2030   ink_filepath_make(input_file_full_path, sizeof(input_file_full_path), TSConfigDirGet(), INPUT_TEXT_FILE);
2031 
2032   // open existing file for reading
2033   if (!(source_read_file = TSfopen(input_file_full_path, "r"))) {
2034     SDK_RPRINT(test, "TSfopen", "TestCase1", TC_FAIL, "can't open file for reading");
2035 
2036     // no need to continue, return
2037     *pstatus = REGRESSION_TEST_FAILED;
2038     return;
2039   } else {
2040     SDK_RPRINT(test, "TSfopen", "TestCase1", TC_PASS, "ok");
2041   }
2042 
2043   // Create unique tmp _file_name_, do not use any TS file_name
2044   snprintf(write_file_name, PATH_NAME_MAX, "/tmp/%sXXXXXX", PFX);
2045   int write_file_fd; // this file will be reopened below
2046   if ((write_file_fd = mkstemp(write_file_name)) <= 0) {
2047     SDK_RPRINT(test, "mkstemp", "std func", TC_FAIL, "can't create file for writing");
2048 
2049     // no need to continue, return
2050     *pstatus = REGRESSION_TEST_FAILED;
2051     if (source_read_file != nullptr) {
2052       TSfclose(source_read_file);
2053     }
2054     return;
2055   }
2056   close(write_file_fd);
2057 
2058   // open file for writing, the file doesn't have to exist.
2059   if (!(write_file = TSfopen(write_file_name, "w"))) {
2060     SDK_RPRINT(test, "TSfopen", "TestCase2", TC_FAIL, "can't open file for writing");
2061 
2062     // no need to continue, return
2063     *pstatus = REGRESSION_TEST_FAILED;
2064     if (source_read_file != nullptr) {
2065       TSfclose(source_read_file);
2066     }
2067     return;
2068   }
2069   SDK_RPRINT(test, "TSfopen", "TestCase2", TC_PASS, "ok");
2070 
2071   memset(input_buffer, '\0', BUFSIZ);
2072 
2073   // source_read_file and input_file_full_path are the same file
2074   if (stat(input_file_full_path, &stat_buffer_input) != 0) {
2075     SDK_RPRINT(test, "stat", "std func", TC_FAIL, "source file and input file messed up");
2076 
2077     // no need to continue, return
2078     *pstatus = REGRESSION_TEST_FAILED;
2079     if (source_read_file != nullptr) {
2080       TSfclose(source_read_file);
2081     }
2082     if (write_file != nullptr) {
2083       TSfclose(write_file);
2084     }
2085     return;
2086   }
2087 
2088   read_amount =
2089     (stat_buffer_input.st_size <= static_cast<off_t>(sizeof(input_buffer))) ? (stat_buffer_input.st_size) : (sizeof(input_buffer));
2090 
2091   // TSfgets
2092   if ((ret_val = TSfgets(source_read_file, input_buffer, read_amount)) == nullptr) {
2093     SDK_RPRINT(test, "TSfgets", "TestCase1", TC_FAIL, "can't read from file");
2094 
2095     // no need to continue, return
2096     *pstatus = REGRESSION_TEST_FAILED;
2097     if (source_read_file != nullptr) {
2098       TSfclose(source_read_file);
2099     }
2100     if (write_file != nullptr) {
2101       TSfclose(write_file);
2102     }
2103     return;
2104   } else {
2105     if (ret_val != input_buffer) {
2106       SDK_RPRINT(test, "TSfgets", "TestCase2", TC_FAIL, "reading error");
2107 
2108       // no need to continue, return
2109       *pstatus = REGRESSION_TEST_FAILED;
2110       if (source_read_file != nullptr) {
2111         TSfclose(source_read_file);
2112       }
2113       if (write_file != nullptr) {
2114         TSfclose(write_file);
2115       }
2116       return;
2117     } else {
2118       SDK_RPRINT(test, "TSfgets", "TestCase1", TC_PASS, "ok");
2119     }
2120   }
2121 
2122   // TSfwrite
2123   wrote = TSfwrite(write_file, input_buffer, read_amount);
2124   if (wrote != read_amount) {
2125     SDK_RPRINT(test, "TSfwrite", "TestCase1", TC_FAIL, "writing error");
2126 
2127     // no need to continue, return
2128     *pstatus = REGRESSION_TEST_FAILED;
2129     if (source_read_file != nullptr) {
2130       TSfclose(source_read_file);
2131     }
2132     if (write_file != nullptr) {
2133       TSfclose(write_file);
2134     }
2135     return;
2136   }
2137 
2138   SDK_RPRINT(test, "TSfwrite", "TestCase1", TC_PASS, "ok");
2139 
2140   // TSfflush
2141   if (stat(write_file_name, &stat_buffer_pre) != 0) {
2142     SDK_RPRINT(test, "stat", "std func", TC_FAIL, "TSfwrite error");
2143 
2144     // no need to continue, return
2145     *pstatus = REGRESSION_TEST_FAILED;
2146     if (source_read_file != nullptr) {
2147       TSfclose(source_read_file);
2148     }
2149     if (write_file != nullptr) {
2150       TSfclose(write_file);
2151     }
2152     return;
2153   }
2154 
2155   TSfflush(write_file); // write_file should point to write_file_name
2156 
2157   if (stat(write_file_name, &stat_buffer_post) != 0) {
2158     SDK_RPRINT(test, "stat", "std func", TC_FAIL, "TSfflush error");
2159 
2160     // no need to continue, return
2161     *pstatus = REGRESSION_TEST_FAILED;
2162     if (source_read_file != nullptr) {
2163       TSfclose(source_read_file);
2164     }
2165     if (write_file != nullptr) {
2166       TSfclose(write_file);
2167     }
2168     return;
2169   }
2170 
2171   if ((stat_buffer_pre.st_size == 0) && (stat_buffer_post.st_size == read_amount)) {
2172     SDK_RPRINT(test, "TSfflush", "TestCase1", TC_PASS, "ok");
2173   } else {
2174     SDK_RPRINT(test, "TSfflush", "TestCase1", TC_FAIL, "TSfflush error");
2175 
2176     // no need to continue, return
2177     *pstatus = REGRESSION_TEST_FAILED;
2178     if (source_read_file != nullptr) {
2179       TSfclose(source_read_file);
2180     }
2181     if (write_file != nullptr) {
2182       TSfclose(write_file);
2183     }
2184     return;
2185   }
2186 
2187   // TSfread
2188   // open again for reading
2189   cmp_read_file = TSfopen(write_file_name, "r");
2190   if (cmp_read_file == nullptr) {
2191     SDK_RPRINT(test, "TSfopen", "TestCase3", TC_FAIL, "can't open file for reading");
2192 
2193     // no need to continue, return
2194     *pstatus = REGRESSION_TEST_FAILED;
2195     if (source_read_file != nullptr) {
2196       TSfclose(source_read_file);
2197     }
2198     if (write_file != nullptr) {
2199       TSfclose(write_file);
2200     }
2201     return;
2202   }
2203 
2204   read_amount =
2205     (stat_buffer_input.st_size <= static_cast<off_t>(sizeof(cmp_buffer))) ? (stat_buffer_input.st_size) : (sizeof(cmp_buffer));
2206 
2207   // TSfread on read file
2208   read = TSfread(cmp_read_file, cmp_buffer, read_amount);
2209   if (read != read_amount) {
2210     SDK_RPRINT(test, "TSfread", "TestCase1", TC_FAIL, "can't reading");
2211 
2212     // no need to continue, return
2213     *pstatus = REGRESSION_TEST_FAILED;
2214     if (source_read_file != nullptr) {
2215       TSfclose(source_read_file);
2216     }
2217     if (write_file != nullptr) {
2218       TSfclose(write_file);
2219     }
2220     if (cmp_read_file != nullptr) {
2221       TSfclose(cmp_read_file);
2222     }
2223     return;
2224   } else {
2225     SDK_RPRINT(test, "TSfread", "TestCase1", TC_PASS, "ok");
2226   }
2227 
2228   // compare input_buffer and cmp_buffer buffers
2229   if (memcmp(input_buffer, cmp_buffer, read_amount) != 0) {
2230     SDK_RPRINT(test, "TSfread", "TestCase2", TC_FAIL, "reading error");
2231 
2232     // no need to continue, return
2233     *pstatus = REGRESSION_TEST_FAILED;
2234     if (source_read_file != nullptr) {
2235       TSfclose(source_read_file);
2236     }
2237     if (write_file != nullptr) {
2238       TSfclose(write_file);
2239     }
2240     if (cmp_read_file != nullptr) {
2241       TSfclose(cmp_read_file);
2242     }
2243     return;
2244   } else {
2245     SDK_RPRINT(test, "TSfread", "TestCase2", TC_PASS, "ok");
2246   }
2247 
2248   // remove the tmp file
2249   if (unlink(write_file_name) != 0) {
2250     SDK_RPRINT(test, "unlink", "std func", TC_FAIL, "can't remove temp file");
2251   }
2252   // TSfclose on read file
2253   TSfclose(source_read_file);
2254   SDK_RPRINT(test, "TSfclose", "TestCase1", TC_PASS, "ok");
2255 
2256   // TSfclose on write file
2257   TSfclose(write_file);
2258   SDK_RPRINT(test, "TSfclose", "TestCase2", TC_PASS, "ok");
2259 
2260   *pstatus = REGRESSION_TEST_PASSED;
2261   if (cmp_read_file != nullptr) {
2262     TSfclose(cmp_read_file);
2263   }
2264 }
2265 
2266 /* TSThread */
2267 
2268 //////////////////////////////////////////////
2269 //       SDK_API_TSThread
2270 //
2271 // Unit Test for API: TSThread
2272 //                    TSThreadCreate
2273 //                    TSThreadSelf
2274 //////////////////////////////////////////////
2275 static int thread_err_count = 0;
2276 static RegressionTest *SDK_Thread_test;
2277 static int *SDK_Thread_pstatus;
2278 static void *thread_create_handler(void *arg);
2279 
2280 static void *
2281 thread_create_handler(void * /* arg ATS_UNUSED */)
2282 {
2283   TSThread athread;
2284   // Fix me: do more useful work
2285   sleep(10);
2286 
2287   athread = TSThreadSelf();
2288   if (athread == nullptr) {
2289     thread_err_count++;
2290     SDK_RPRINT(SDK_Thread_test, "TSThreadCreate", "TestCase2", TC_FAIL, "can't get thread");
2291   } else {
2292     SDK_RPRINT(SDK_Thread_test, "TSThreadCreate", "TestCase2", TC_PASS, "ok");
2293   }
2294 
2295   if (thread_err_count > 0) {
2296     *SDK_Thread_pstatus = REGRESSION_TEST_FAILED;
2297   } else {
2298     *SDK_Thread_pstatus = REGRESSION_TEST_PASSED;
2299   }
2300 
2301   return nullptr;
2302 }
2303 
2304 // Fix me: Solaris threads/Win2K threads tests
2305 
2306 // Argument data passed to thread init functions
2307 //  cannot be allocated on the stack.
2308 
2309 REGRESSION_TEST(SDK_API_TSThread)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2310 {
2311   *pstatus           = REGRESSION_TEST_INPROGRESS;
2312   SDK_Thread_test    = test;
2313   SDK_Thread_pstatus = pstatus;
2314 
2315   TSThread curr_thread = nullptr;
2316   //    TSThread created_thread = 0;
2317   pthread_t curr_tid;
2318 
2319   curr_tid = pthread_self();
2320 
2321   // TSThreadSelf
2322   curr_thread = TSThreadSelf();
2323   if (curr_thread == nullptr) {
2324     SDK_RPRINT(test, "TSThreadSelf", "TestCase1", TC_FAIL, "can't get the current thread");
2325     thread_err_count++;
2326   } else {
2327     SDK_RPRINT(test, "TSThreadSelf", "TestCase1", TC_PASS, "ok");
2328   }
2329 
2330   // TSThreadCreate
2331   TSThread created_thread = TSThreadCreate(thread_create_handler, (void *)static_cast<intptr_t>(curr_tid));
2332   if (created_thread == nullptr) {
2333     thread_err_count++;
2334     SDK_RPRINT(test, "TSThreadCreate", "TestCase1", TC_FAIL, "can't create thread");
2335   } else {
2336     SDK_RPRINT(test, "TSThreadCreate", "TestCase1", TC_PASS, "ok");
2337   }
2338 
2339   if (created_thread != nullptr) {
2340     TSThreadWait(created_thread);
2341     TSThreadDestroy(created_thread);
2342   }
2343 }
2344 
2345 //////////////////////////////////////////////
2346 //       SDK_API_TSThread
2347 //
2348 // Unit Test for API: TSThreadInit
2349 //                    TSThreadDestroy
2350 //////////////////////////////////////////////
2351 static int thread_init_err_count = 0;
2352 static RegressionTest *SDK_ThreadInit_test;
2353 static int *SDK_ThreadInit_pstatus;
2354 static void *pthread_start_func(void *arg);
2355 
2356 static void *
2357 pthread_start_func(void * /* arg ATS_UNUSED */)
2358 {
2359   TSThread temp_thread = nullptr;
2360 
2361   // TSThreadInit
2362   temp_thread = TSThreadInit();
2363 
2364   if (!temp_thread) {
2365     SDK_RPRINT(SDK_ThreadInit_test, "TSThreadInit", "TestCase2", TC_FAIL, "can't init thread");
2366     thread_init_err_count++;
2367   } else {
2368     SDK_RPRINT(SDK_ThreadInit_test, "TSThreadInit", "TestCase2", TC_PASS, "ok");
2369   }
2370 
2371   // Clean up this thread
2372   if (temp_thread) {
2373     TSThreadDestroy(temp_thread);
2374   }
2375 
2376   if (thread_init_err_count > 0) {
2377     *SDK_ThreadInit_pstatus = REGRESSION_TEST_FAILED;
2378   } else {
2379     *SDK_ThreadInit_pstatus = REGRESSION_TEST_PASSED;
2380   }
2381 
2382   return nullptr;
2383 }
2384 
2385 REGRESSION_TEST(SDK_API_TSThreadInit)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2386 {
2387   *pstatus               = REGRESSION_TEST_INPROGRESS;
2388   SDK_ThreadInit_test    = test;
2389   SDK_ThreadInit_pstatus = pstatus;
2390 
2391   pthread_t curr_tid, new_tid;
2392 
2393   curr_tid = pthread_self();
2394 
2395   int ret;
2396   errno = 0;
2397   ret   = pthread_create(&new_tid, nullptr, pthread_start_func, (void *)static_cast<intptr_t>(curr_tid));
2398   if (ret != 0) {
2399     thread_init_err_count++;
2400     SDK_RPRINT(test, "TSThreadInit", "TestCase1", TC_FAIL, "can't create pthread");
2401   } else {
2402     SDK_RPRINT(test, "TSThreadInit", "TestCase1", TC_PASS, "ok");
2403   }
2404 }
2405 
2406 /* Action */
2407 
2408 //////////////////////////////////////////////
2409 //       SDK_API_TSAction
2410 //
2411 // Unit Test for API: TSActionCancel
2412 //////////////////////////////////////////////
2413 
2414 static RegressionTest *SDK_ActionCancel_test;
2415 static int *SDK_ActionCancel_pstatus;
2416 
2417 int
2418 action_cancel_handler(TSCont contp, TSEvent event, void * /* edata ATS_UNUSED */)
2419 {
2420   if (event == TS_EVENT_IMMEDIATE) { // called from schedule_imm OK
2421     SDK_RPRINT(SDK_ActionCancel_test, "TSActionCancel", "TestCase1", TC_PASS, "ok");
2422     *SDK_ActionCancel_pstatus = REGRESSION_TEST_PASSED;
2423   } else if (event == TS_EVENT_TIMEOUT) { // called from schedule_in Not OK.
2424     SDK_RPRINT(SDK_ActionCancel_test, "TSActionCancel", "TestCase1", TC_FAIL, "bad action");
2425     *SDK_ActionCancel_pstatus = REGRESSION_TEST_FAILED;
2426   } else { // there is sth wrong
2427     SDK_RPRINT(SDK_ActionCancel_test, "TSActionCancel", "TestCase1", TC_FAIL, "bad event");
2428     *SDK_ActionCancel_pstatus = REGRESSION_TEST_FAILED;
2429   }
2430 
2431   TSContDestroy(contp);
2432   return 0;
2433 }
2434 
2435 REGRESSION_TEST(SDK_API_TSActionCancel)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2436 {
2437   *pstatus = REGRESSION_TEST_INPROGRESS;
2438 
2439   SDK_ActionCancel_test    = test;
2440   SDK_ActionCancel_pstatus = pstatus;
2441 
2442   TSMutex cont_mutex = TSMutexCreate();
2443   TSCont contp       = TSContCreate(action_cancel_handler, cont_mutex);
2444   TSAction actionp   = TSContScheduleOnPool(contp, 10000, TS_THREAD_POOL_NET);
2445 
2446   TSMutexLock(cont_mutex);
2447   if (TSActionDone(actionp)) {
2448     *pstatus = REGRESSION_TEST_FAILED;
2449     TSMutexUnlock(cont_mutex);
2450     return;
2451   } else {
2452     TSActionCancel(actionp);
2453   }
2454   TSMutexUnlock(cont_mutex);
2455 
2456   TSContScheduleOnPool(contp, 0, TS_THREAD_POOL_NET);
2457 }
2458 
2459 //////////////////////////////////////////////
2460 //       SDK_API_TSAction
2461 //
2462 // Unit Test for API: TSActionDone
2463 //////////////////////////////////////////////
2464 /* Currently, don't know how to test it because TSAction
2465    is at "done" status only "shortly" after finish
2466    executing action_done_handler. Another possibility is
2467    to use reentrant call. But in both cases it's not
2468    guaranteed to get ActionDone.
2469    */
2470 
2471 /* Continuations */
2472 
2473 //////////////////////////////////////////////
2474 //       SDK_API_TSCont
2475 //
2476 // Unit Test for API: TSContCreate
2477 //                    TSContCall
2478 //////////////////////////////////////////////
2479 
2480 // this is needed for asynchronous APIs
2481 static RegressionTest *SDK_ContCreate_test;
2482 static int *SDK_ContCreate_pstatus;
2483 
2484 int
2485 cont_handler(TSCont /* contp ATS_UNUSED */, TSEvent /* event ATS_UNUSED */, void * /* edata ATS_UNUSED */)
2486 {
2487   SDK_RPRINT(SDK_ContCreate_test, "TSContCreate", "TestCase1", TC_PASS, "ok");
2488   SDK_RPRINT(SDK_ContCreate_test, "TSContCall", "TestCase1", TC_PASS, "ok");
2489 
2490   *SDK_ContCreate_pstatus = REGRESSION_TEST_PASSED;
2491 
2492   return 0;
2493 }
2494 
2495 REGRESSION_TEST(SDK_API_TSContCreate)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2496 {
2497   *pstatus = REGRESSION_TEST_INPROGRESS;
2498 
2499   // For asynchronous APIs, use static vars to store test and pstatus
2500   SDK_ContCreate_test    = test;
2501   SDK_ContCreate_pstatus = pstatus;
2502 
2503   TSMutex mutexp = TSMutexCreate();
2504   TSCont contp   = TSContCreate(cont_handler, mutexp);
2505 
2506   if (TS_SUCCESS == TSMutexLockTry(mutexp)) { // Mutex is grabbed successfully
2507     TSContCall(contp, static_cast<TSEvent>(0), nullptr);
2508     TSMutexUnlock(mutexp);
2509   } else { // mutex has problems
2510     SDK_RPRINT(SDK_ContCreate_test, "TSContCreate", "TestCase1", TC_FAIL, "continuation creation has problems");
2511     SDK_RPRINT(SDK_ContCreate_test, "TSContCall", "TestCase1", TC_FAIL, "continuation has problems");
2512 
2513     *pstatus = REGRESSION_TEST_FAILED;
2514   }
2515 
2516   TSContDestroy(contp);
2517 }
2518 
2519 //////////////////////////////////////////////
2520 //       SDK_API_TSCont
2521 //
2522 // Unit Test for API: TSContDataGet
2523 //                    TSContDataSet
2524 //////////////////////////////////////////////
2525 
2526 // this is needed for asynchronous APIs
2527 static RegressionTest *SDK_ContData_test;
2528 static int *SDK_ContData_pstatus;
2529 
2530 // this is specific for this test
2531 typedef struct {
2532   int data1;
2533   int data2;
2534 } MyData;
2535 
2536 int
2537 cont_data_handler(TSCont contp, TSEvent /* event ATS_UNUSED */, void * /* edata ATS_UNUSED */)
2538 {
2539   MyData *my_data = static_cast<MyData *>(TSContDataGet(contp));
2540 
2541   if (my_data->data1 == 1 && my_data->data2 == 2) {
2542     SDK_RPRINT(SDK_ContData_test, "TSContDataSet", "TestCase1", TC_PASS, "ok");
2543     SDK_RPRINT(SDK_ContData_test, "TSContDataGet", "TestCase1", TC_PASS, "ok");
2544 
2545     *SDK_ContData_pstatus = REGRESSION_TEST_PASSED;
2546   } else {
2547     // If we get bad data, it's a failure
2548     SDK_RPRINT(SDK_ContData_test, "TSContDataSet", "TestCase1", TC_FAIL, "bad data");
2549     SDK_RPRINT(SDK_ContData_test, "TSContDataGet", "TestCase1", TC_FAIL, "bad data");
2550 
2551     *SDK_ContData_pstatus = REGRESSION_TEST_FAILED;
2552   }
2553 
2554   TSfree(my_data);
2555   TSContDestroy(contp);
2556   return 0;
2557 }
2558 
2559 REGRESSION_TEST(SDK_API_TSContDataGet)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2560 {
2561   *pstatus = REGRESSION_TEST_INPROGRESS;
2562 
2563   // For asynchronous APIs, use static vars to store test and pstatus
2564   SDK_ContData_test    = test;
2565   SDK_ContData_pstatus = pstatus;
2566 
2567   TSCont contp = TSContCreate(cont_data_handler, TSMutexCreate());
2568 
2569   MyData *my_data = static_cast<MyData *>(TSmalloc(sizeof(MyData)));
2570   my_data->data1  = 1;
2571   my_data->data2  = 2;
2572 
2573   TSContDataSet(contp, (void *)my_data);
2574 
2575   TSContScheduleOnPool(contp, 0, TS_THREAD_POOL_NET);
2576 }
2577 
2578 //////////////////////////////////////////////
2579 //       SDK_API_TSCont
2580 //
2581 // Unit Test for API: TSContMutexGet
2582 //////////////////////////////////////////////
2583 
2584 REGRESSION_TEST(SDK_API_TSContMutexGet)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2585 {
2586   bool test_passed = false;
2587   *pstatus         = REGRESSION_TEST_INPROGRESS;
2588 
2589   TSMutex mutexp_input;
2590   TSMutex mutexp_output;
2591   TSCont contp;
2592 
2593   mutexp_input = TSMutexCreate();
2594   contp        = TSContCreate(cont_handler, mutexp_input);
2595 
2596   mutexp_output = TSContMutexGet(contp);
2597 
2598   if (mutexp_input == mutexp_output) {
2599     SDK_RPRINT(test, "TSContMutexGet", "TestCase1", TC_PASS, "ok");
2600     test_passed = true;
2601   } else {
2602     SDK_RPRINT(test, "TSContMutexGet", "TestCase1", TC_FAIL, "Continuation's mutex corrupted");
2603   }
2604 
2605   // Status of the whole test
2606   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2607 
2608   TSContDestroy(contp);
2609 }
2610 
2611 //////////////////////////////////////////////
2612 //       SDK_API_TSCont
2613 //
2614 // Unit Test for API: TSContScheduleOnPool
2615 //////////////////////////////////////////////
2616 
2617 // this is needed for asynchronous APIs
2618 static RegressionTest *SDK_ContSchedule_test;
2619 static int *SDK_ContSchedule_pstatus;
2620 
2621 // this is specific for this test
2622 static int tc1_count = 0;
2623 static int tc2_count = 0;
2624 
2625 int
2626 cont_schedule_handler(TSCont contp, TSEvent event, void * /* edata ATS_UNUSED */)
2627 {
2628   if (event == TS_EVENT_IMMEDIATE) {
2629     // Test Case 1
2630     SDK_RPRINT(SDK_ContSchedule_test, "TSContScheduleOnPool", "TestCase1", TC_PASS, "ok");
2631     tc1_count++;
2632   } else if (event == TS_EVENT_TIMEOUT) {
2633     // Test Case 2
2634     SDK_RPRINT(SDK_ContSchedule_test, "TSContScheduleOnPool", "TestCase2", TC_PASS, "ok");
2635     tc2_count++;
2636   } else {
2637     // If we receive a bad event, it's a failure
2638     SDK_RPRINT(SDK_ContSchedule_test, "TSContScheduleOnPool", "TestCase1|2", TC_FAIL, "received unexpected event number %d", event);
2639     *SDK_ContSchedule_pstatus = REGRESSION_TEST_FAILED;
2640     return 0;
2641   }
2642 
2643   // We expect to be called once for TC1 and once for TC2
2644   if ((tc1_count == 1) && (tc2_count == 1)) {
2645     *SDK_ContSchedule_pstatus = REGRESSION_TEST_PASSED;
2646   }
2647   // If TC1 or TC2 executed more than once, something is fishy..
2648   else if (tc1_count + tc2_count >= 2) {
2649     *SDK_ContSchedule_pstatus = REGRESSION_TEST_FAILED;
2650   }
2651 
2652   TSContDestroy(contp);
2653   return 0;
2654 }
2655 
2656 /* Mutex */
2657 
2658 /*
2659    Fix me: test for grabbing the mutex from two
2660    different threads.
2661    */
2662 
2663 //////////////////////////////////////////////
2664 //       SDK_API_TSMutex
2665 //
2666 // Unit Test for API: TSMutexCreate
2667 //                    TSMutexLock
2668 //                    TSMutexUnLock
2669 //////////////////////////////////////////////
2670 
2671 REGRESSION_TEST(SDK_API_TSMutexCreate)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2672 {
2673   bool test_passed = false;
2674   *pstatus         = REGRESSION_TEST_INPROGRESS;
2675 
2676   TSMutex mutexp = TSMutexCreate();
2677 
2678   TSMutexLock(mutexp);
2679 
2680   /* This is normal because all locking is from the same thread */
2681   TSReturnCode lock1 = TS_ERROR;
2682   TSReturnCode lock2 = TS_ERROR;
2683 
2684   lock1 = TSMutexLockTry(mutexp);
2685   lock2 = TSMutexLockTry(mutexp);
2686 
2687   if (TS_SUCCESS == lock1 && TS_SUCCESS == lock2) {
2688     SDK_RPRINT(test, "TSMutexCreate", "TestCase1", TC_PASS, "ok");
2689     SDK_RPRINT(test, "TSMutexLock", "TestCase1", TC_PASS, "ok");
2690     SDK_RPRINT(test, "TSMutexLockTry", "TestCase1", TC_PASS, "ok");
2691     test_passed = true;
2692   } else {
2693     SDK_RPRINT(test, "TSMutexCreate", "TestCase1", TC_FAIL, "mutex can't be grabbed twice from the same thread");
2694     SDK_RPRINT(test, "TSMutexLock", "TestCase1", TC_FAIL, "mutex can't be grabbed twice from the same thread");
2695     SDK_RPRINT(test, "TSMutexLockTry", "TestCase1", TC_FAIL, "mutex can't be grabbed twice from the same thread");
2696   }
2697 
2698   TSMutexUnlock(mutexp);
2699   SDK_RPRINT(test, "TSMutexUnLock", "TestCase1", TC_PASS, "ok");
2700 
2701   if (test_passed) {
2702     *pstatus = REGRESSION_TEST_PASSED;
2703   } else {
2704     *pstatus = REGRESSION_TEST_FAILED;
2705   }
2706 }
2707 
2708 /* IOBuffer */
2709 
2710 //////////////////////////////////////////////
2711 //       SDK_API_TSIOBuffer
2712 //
2713 // Unit Test for API: TSIOBufferCreate
2714 //                    TSIOBufferWaterMarkGet
2715 //                    TSIOBufferWaterMarkSet
2716 //////////////////////////////////////////////
2717 
2718 REGRESSION_TEST(SDK_API_TSIOBufferCreate)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2719 {
2720   bool test_passed = false;
2721   *pstatus         = REGRESSION_TEST_INPROGRESS;
2722 
2723   int64_t watermark = 1000;
2724 
2725   TSIOBuffer bufp = TSIOBufferCreate();
2726 
2727   TSIOBufferWaterMarkSet(bufp, watermark);
2728   watermark = TSIOBufferWaterMarkGet(bufp);
2729 
2730   if (watermark == 1000) {
2731     SDK_RPRINT(test, "TSIOBufferCreate", "TestCase1", TC_PASS, "ok");
2732     SDK_RPRINT(test, "TSIOBufferWaterMarkGet", "TestCase1", TC_PASS, "ok");
2733     SDK_RPRINT(test, "TSIOBufferWaterMarkSet", "TestCase1", TC_PASS, "ok");
2734     test_passed = true;
2735   } else {
2736     SDK_RPRINT(test, "TSIOBufferCreate", "TestCase1", TC_FAIL, "watermark failed");
2737     SDK_RPRINT(test, "TSIOBufferWaterMarkGet", "TestCase1", TC_FAIL, "watermark failed");
2738     SDK_RPRINT(test, "TSIOBufferWaterMarkSet", "TestCase1", TC_FAIL, "watermark failed");
2739   }
2740 
2741   TSIOBufferDestroy(bufp);
2742 
2743   // Status of the whole test
2744   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2745   return;
2746 }
2747 
2748 //////////////////////////////////////////////
2749 //       SDK_API_TSIOBuffer
2750 //
2751 // Unit Test for API: TSIOBufferSizedCreate
2752 //                    TSIOBufferProduce
2753 //                    TSIOBufferReaderAlloc
2754 //                    TSIOBufferReaderAvail
2755 //////////////////////////////////////////////
2756 
2757 REGRESSION_TEST(SDK_API_TSIOBufferProduce)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2758 {
2759   bool test_passed = false;
2760   *pstatus         = REGRESSION_TEST_INPROGRESS;
2761 
2762   TSIOBuffer bufp = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_4K); // size is 4096
2763 
2764   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2765 
2766   TSIOBufferProduce(bufp, 10);
2767 
2768   int64_t reader_avail = TSIOBufferReaderAvail(readerp);
2769   if (reader_avail == 10) {
2770     SDK_RPRINT(test, "TSIOBufferProduce", "TestCase1", TC_PASS, "ok");
2771     SDK_RPRINT(test, "TSIOBufferReaderAlloc", "TestCase1", TC_PASS, "ok");
2772     SDK_RPRINT(test, "TSIOBufferReaderAvail", "TestCase1", TC_PASS, "ok");
2773     test_passed = true;
2774   } else {
2775     SDK_RPRINT(test, "TSIOBufferProduce", "TestCase1", TC_FAIL, "failed");
2776     SDK_RPRINT(test, "TSIOBufferReaderAlloc", "TestCase1", TC_FAIL, "failed");
2777     SDK_RPRINT(test, "TSIOBufferReaderAvail", "TestCase1", TC_FAIL, "failed");
2778   }
2779 
2780   // Status of the whole test
2781   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2782   return;
2783 }
2784 
2785 //////////////////////////////////////////////
2786 //       SDK_API_TSIOBuffer
2787 //
2788 // Unit Test for API: TSIOBufferReaderConsume
2789 //////////////////////////////////////////////
2790 
2791 REGRESSION_TEST(SDK_API_TSIOBufferReaderConsume)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2792 {
2793   bool test_passed = false;
2794   *pstatus         = REGRESSION_TEST_INPROGRESS;
2795 
2796   TSIOBuffer bufp = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_4K);
2797 
2798   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2799 
2800   TSIOBufferProduce(bufp, 10);
2801   TSIOBufferReaderConsume(readerp, 10);
2802 
2803   int64_t reader_avail = TSIOBufferReaderAvail(readerp);
2804   if (reader_avail == 0) {
2805     SDK_RPRINT(test, "TSIOBufferReaderConsume", "TestCase1", TC_PASS, "ok");
2806     test_passed = true;
2807   } else {
2808     SDK_RPRINT(test, "TSIOBufferReaderConsume", "TestCase1", TC_FAIL, "failed");
2809   }
2810 
2811   // Status of the whole test
2812   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2813   return;
2814 }
2815 
2816 //////////////////////////////////////////////
2817 //       SDK_API_TSIOBuffer
2818 //
2819 // Unit Test for API: TSIOBufferReaderClone
2820 //////////////////////////////////////////////
2821 
2822 REGRESSION_TEST(SDK_API_TSIOBufferReaderClone)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2823 {
2824   bool test_passed = false;
2825   *pstatus         = REGRESSION_TEST_INPROGRESS;
2826 
2827   TSIOBuffer bufp          = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_4K);
2828   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2829 
2830   TSIOBufferProduce(bufp, 10);
2831   TSIOBufferReaderConsume(readerp, 5);
2832 
2833   TSIOBufferReader readerp2 = TSIOBufferReaderClone(readerp);
2834 
2835   int64_t reader_avail = TSIOBufferReaderAvail(readerp2);
2836   if (reader_avail == 5) {
2837     SDK_RPRINT(test, "TSIOBufferReaderClone", "TestCase1", TC_PASS, "ok");
2838     test_passed = true;
2839   } else {
2840     SDK_RPRINT(test, "TSIOBufferReaderClone", "TestCase1", TC_FAIL, "failed");
2841   }
2842 
2843   // Status of the whole test
2844   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2845   return;
2846 }
2847 
2848 //////////////////////////////////////////////
2849 //       SDK_API_TSIOBuffer
2850 //
2851 // Unit Test for API: TSIOBufferStart
2852 //                    TSIOBufferReaderStart
2853 //////////////////////////////////////////////
2854 
2855 REGRESSION_TEST(SDK_API_TSIOBufferStart)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2856 {
2857   bool test_passed = false;
2858   *pstatus         = REGRESSION_TEST_INPROGRESS;
2859 
2860   TSIOBuffer bufp = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_4K);
2861 
2862   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2863 
2864   if (TSIOBufferStart(bufp) == TSIOBufferReaderStart(readerp)) {
2865     SDK_RPRINT(test, "TSIOBufferStart", "TestCase1", TC_PASS, "ok");
2866     SDK_RPRINT(test, "TSIOBufferReaderStart", "TestCase1", TC_PASS, "ok");
2867     test_passed = true;
2868   } else {
2869     SDK_RPRINT(test, "TSIOBufferStart", "TestCase1", TC_FAIL, "failed");
2870     SDK_RPRINT(test, "TSIOBufferReaderStart", "TestCase1", TC_FAIL, "failed");
2871   }
2872 
2873   // Status of the whole test
2874   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2875   return;
2876 }
2877 
2878 //////////////////////////////////////////////
2879 //       SDK_API_TSIOBuffer
2880 //
2881 // Unit Test for API: TSIOBufferCopy
2882 //                    TSIOBufferWrite
2883 //                    TSIOBufferReaderCopy
2884 //////////////////////////////////////////////
2885 
2886 REGRESSION_TEST(SDK_API_TSIOBufferCopy)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2887 {
2888   bool test_passed = false;
2889   *pstatus         = REGRESSION_TEST_INPROGRESS;
2890 
2891   char input_buf[] = "This is the test for TSIOBufferCopy, TSIOBufferWrite, TSIOBufferReaderCopy";
2892   char output_buf[1024];
2893   TSIOBuffer bufp  = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_4K);
2894   TSIOBuffer bufp2 = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_4K);
2895 
2896   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2897   TSIOBufferWrite(bufp, input_buf, (strlen(input_buf) + 1));
2898   TSIOBufferCopy(bufp2, readerp, (strlen(input_buf) + 1), 0);
2899   TSIOBufferReaderCopy(readerp, output_buf, (strlen(input_buf) + 1));
2900 
2901   if (strcmp(input_buf, output_buf) == 0) {
2902     SDK_RPRINT(test, "TSIOBufferWrite", "TestCase1", TC_PASS, "ok");
2903     SDK_RPRINT(test, "TSIOBufferCopy", "TestCase1", TC_PASS, "ok");
2904     SDK_RPRINT(test, "TSIOBufferReaderCopy", "TestCase1", TC_PASS, "ok");
2905     test_passed = true;
2906   } else {
2907     SDK_RPRINT(test, "TSIOBufferWrite", "TestCase1", TC_FAIL, "failed");
2908     SDK_RPRINT(test, "TSIOBufferCopy", "TestCase1", TC_FAIL, "failed");
2909     SDK_RPRINT(test, "TSIOBufferReaderCopy", "TestCase1", TC_FAIL, "failed");
2910   }
2911 
2912   // Status of the whole test
2913   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2914   return;
2915 }
2916 
2917 //////////////////////////////////////////////
2918 //       SDK_API_TSIOBuffer
2919 //
2920 // Unit Test for API: TSIOBuffer
2921 //                    TSIOBufferWrite
2922 //                    TSIOBufferReaderCopy
2923 //////////////////////////////////////////////
2924 
2925 REGRESSION_TEST(SDK_API_TSIOBufferBlockReadAvail)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2926 {
2927   bool test_passed_1 = false;
2928   bool test_passed_2 = false;
2929   *pstatus           = REGRESSION_TEST_INPROGRESS;
2930 
2931   int i           = 10000;
2932   TSIOBuffer bufp = TSIOBufferCreate();
2933   TSIOBufferWrite(bufp, reinterpret_cast<char *>(&i), sizeof(int));
2934   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2935 
2936   int64_t avail_write, avail_read;
2937 
2938   // TODO: This is probably not correct any more.
2939   TSIOBufferBlock blockp = TSIOBufferStart(bufp);
2940 
2941   if ((TSIOBufferBlockWriteStart(blockp, &avail_write) - TSIOBufferBlockReadStart(blockp, readerp, &avail_read)) == sizeof(int)) {
2942     SDK_RPRINT(test, "TSIOBufferBlockReadStart", "TestCase1", TC_PASS, "ok");
2943     SDK_RPRINT(test, "TSIOBufferBlockWriteStart", "TestCase1", TC_PASS, "ok");
2944     test_passed_1 = true;
2945   } else {
2946     SDK_RPRINT(test, "TSIOBufferBlockReadStart", "TestCase1", TC_FAIL, "failed");
2947     SDK_RPRINT(test, "TSIOBufferBlockWriteStart", "TestCase1", TC_FAIL, "failed");
2948   }
2949 
2950   if ((TSIOBufferBlockReadAvail(blockp, readerp) + TSIOBufferBlockWriteAvail(blockp)) == 4096) {
2951     SDK_RPRINT(test, "TSIOBufferBlockReadAvail", "TestCase1", TC_PASS, "ok");
2952     SDK_RPRINT(test, "TSIOBufferBlockWriteAvail", "TestCase1", TC_PASS, "ok");
2953     test_passed_2 = true;
2954   } else {
2955     SDK_RPRINT(test, "TSIOBufferBlockReadAvail", "TestCase1", TC_FAIL, "failed");
2956     SDK_RPRINT(test, "TSIOBufferBlockWriteAvail", "TestCase1", TC_FAIL, "failed");
2957   }
2958 
2959   if (test_passed_1 && test_passed_2) {
2960     *pstatus = REGRESSION_TEST_PASSED;
2961   } else {
2962     *pstatus = REGRESSION_TEST_FAILED;
2963   }
2964 
2965   return;
2966 }
2967 
2968 //////////////////////////////////////////////////
2969 //       SDK_API_TSIOBuffer
2970 //
2971 // Unit Test for API: TSIOBufferBlockNext
2972 //////////////////////////////////////////////////
2973 
2974 REGRESSION_TEST(SDK_API_TSIOBufferBlockNext)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2975 {
2976   bool test_passed = false;
2977   *pstatus         = REGRESSION_TEST_INPROGRESS;
2978 
2979   int i           = 10000;
2980   TSIOBuffer bufp = TSIOBufferCreate();
2981   TSIOBufferWrite(bufp, reinterpret_cast<char *>(&i), sizeof(int));
2982 
2983   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2984   TSIOBufferBlock blockp   = TSIOBufferReaderStart(readerp);
2985 
2986   // TODO: This is probably not the best of regression tests right now ...
2987   // Note that this assumes block size is > sizeof(int) bytes.
2988   if (TSIOBufferBlockNext(blockp) == nullptr) {
2989     SDK_RPRINT(test, "TSIOBufferBlockNext", "TestCase1", TC_PASS, "ok");
2990     test_passed = true;
2991   } else {
2992     SDK_RPRINT(test, "TSIOBufferBlockNext", "TestCase1", TC_FAIL, "fail");
2993   }
2994 
2995   if (test_passed) {
2996     *pstatus = REGRESSION_TEST_PASSED;
2997   } else {
2998     *pstatus = REGRESSION_TEST_FAILED;
2999   }
3000 
3001   return;
3002 }
3003 
3004 REGRESSION_TEST(SDK_API_TSContSchedule)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
3005 {
3006   *pstatus = REGRESSION_TEST_INPROGRESS;
3007 
3008   // For asynchronous APIs, use static vars to store test and pstatus
3009   SDK_ContSchedule_test    = test;
3010   SDK_ContSchedule_pstatus = pstatus;
3011 
3012   TSCont contp  = TSContCreate(cont_schedule_handler, TSMutexCreate());
3013   TSCont contp2 = TSContCreate(cont_schedule_handler, TSMutexCreate());
3014 
3015   // Test Case 1: schedule immediate
3016   TSContScheduleOnPool(contp, 0, TS_THREAD_POOL_NET);
3017 
3018   // Test Case 2: schedule in 10ms
3019   TSContScheduleOnPool(contp2, 10, TS_THREAD_POOL_NET);
3020 }
3021 
3022 //////////////////////////////////////////////////////////////////////////////
3023 //     SDK_API_HttpHookAdd
3024 //
3025 // Unit Test for API: TSHttpHookAdd
3026 //                    TSHttpTxnReenable
3027 //                    TSHttpTxnClientIPGet
3028 //                    TSHttpTxnServerIPGet
3029 //                    TSHttpTxnIncomingAddrGet
3030 //                    TSHttpTxnClientAddrGet
3031 //                    TSHttpTxnClientReqGet
3032 //                    TSHttpTxnClientRespGet
3033 //                    TSHttpTxnServerReqGet
3034 //                    TSHttpTxnServerRespGet
3035 //                    TSHttpTxnNextHopAddrGet
3036 //                    TSHttpTxnClientProtocolStackGet
3037 //                    TSHttpTxnClientProtocolStackContains
3038 //////////////////////////////////////////////////////////////////////////////
3039 
3040 #define HTTP_HOOK_TEST_REQUEST_ID 1
3041 
3042 typedef struct {
3043   RegressionTest *regtest;
3044   int *pstatus;
3045   SocketServer *os;
3046   ClientTxn *browser;
3047   int hook_mask;
3048   int reenable_mask;
3049   bool test_client_ip_get;
3050   bool test_client_incoming_port_get;
3051   bool test_client_remote_port_get;
3052   bool test_client_req_get;
3053   bool test_client_resp_get;
3054   bool test_server_ip_get;
3055   bool test_server_req_get;
3056   bool test_server_resp_get;
3057   bool test_next_hop_ip_get;
3058   bool test_client_protocol_stack_get;
3059   bool test_client_protocol_stack_contains;
3060 
3061   unsigned int magic;
3062 } SocketTest;
3063 
3064 // This func is called by us from mytest_handler to test TSHttpTxnClientIPGet
3065 static int
3066 checkHttpTxnClientIPGet(SocketTest *test, void *data)
3067 {
3068   sockaddr const *ptr;
3069   in_addr_t ip;
3070   TSHttpTxn txnp      = static_cast<TSHttpTxn>(data);
3071   in_addr_t actual_ip = htonl(INADDR_LOOPBACK); /* 127.0.0.1 is expected because the client is on the same machine */
3072 
3073   ptr = TSHttpTxnClientAddrGet(txnp);
3074   if (ptr == nullptr || INADDR_ANY == (ip = ats_ip4_addr_cast(ptr))) {
3075     test->test_client_ip_get = false;
3076     SDK_RPRINT(test->regtest, "TSHttpTxnClientIPGet", "TestCase1", TC_FAIL, "TSHttpTxnClientIPGet returns 0 %s",
3077                ptr ? "address" : "pointer");
3078     return TS_EVENT_CONTINUE;
3079   }
3080 
3081   if (ip == actual_ip) {
3082     test->test_client_ip_get = true;
3083     SDK_RPRINT(test->regtest, "TSHttpTxnClientIPGet", "TestCase1", TC_PASS, "ok [%0.8x]", ip);
3084   } else {
3085     test->test_client_ip_get = false;
3086     SDK_RPRINT(test->regtest, "TSHttpTxnClientIPGet", "TestCase1", TC_FAIL, "Value's Mismatch [expected %.8x got %.8x]", actual_ip,
3087                ip);
3088   }
3089   return TS_EVENT_CONTINUE;
3090 }
3091 
3092 // This func is called by us from mytest_handler to check for TSHttpTxnClientProtocolStackGet
3093 static int
3094 checkHttpTxnClientProtocolStackGet(SocketTest *test, void *data)
3095 {
3096   TSHttpTxn txnp = static_cast<TSHttpTxn>(data);
3097   const char *results[10];
3098   int count = 0;
3099   TSHttpTxnClientProtocolStackGet(txnp, 10, results, &count);
3100   // Should return results[0] = "http/1.0", results[1] = "tcp", results[2] = "ipv4"
3101   test->test_client_protocol_stack_get = true;
3102   if (count != 3) {
3103     test->test_client_protocol_stack_get = false;
3104     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackGet", "TestCase1", TC_FAIL, "count should be 3 is %d", count);
3105   } else if (strcmp(results[0], "http/1.0") != 0) {
3106     test->test_client_protocol_stack_get = false;
3107     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackGet", "TestCase1", TC_FAIL, "results[0] should be http/1.0 is %s",
3108                results[0]);
3109   } else if (strcmp(results[1], "tcp") != 0) {
3110     test->test_client_protocol_stack_get = false;
3111     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackGet", "TestCase1", TC_FAIL, "results[1] should be tcp is %s",
3112                results[1]);
3113   } else if (strcmp(results[2], "ipv4") != 0) {
3114     test->test_client_protocol_stack_get = false;
3115     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackGet", "TestCase1", TC_FAIL, "results[2] should be ipv4 is %s",
3116                results[2]);
3117   } else {
3118     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackGet", "TestCase1", TC_PASS, "ok stack_size=%d", count);
3119   }
3120   return TS_EVENT_CONTINUE;
3121 }
3122 
3123 // This func is called by us from mytest_handler to check for TSHttpTxnClientProtocolStackContains
3124 static int
3125 checkHttpTxnClientProtocolStackContains(SocketTest *test, void *data)
3126 {
3127   TSHttpTxn txnp                            = static_cast<TSHttpTxn>(data);
3128   const char *ret_tag                       = TSHttpTxnClientProtocolStackContains(txnp, "tcp");
3129   test->test_client_protocol_stack_contains = true;
3130   if (ret_tag) {
3131     const char *normalized_tag = TSNormalizedProtocolTag("tcp");
3132     if (normalized_tag != ret_tag) {
3133       SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackContains", "TestCase1", TC_FAIL,
3134                  "contains tcp, but normalized tag is wrong");
3135     } else {
3136       SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackContains", "TestCase1", TC_PASS, "ok tcp");
3137     }
3138   } else {
3139     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackContains", "TestCase1", TC_FAIL, "missing tcp");
3140     test->test_client_protocol_stack_contains = false;
3141   }
3142   ret_tag = TSHttpTxnClientProtocolStackContains(txnp, "udp");
3143   if (!ret_tag) {
3144     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackContains", "TestCase2", TC_PASS, "ok no udp");
3145   } else {
3146     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackContains", "TestCase2", TC_FAIL, "faulty udp report");
3147     test->test_client_protocol_stack_contains = false;
3148   }
3149   return TS_EVENT_CONTINUE;
3150 }
3151 
3152 // This func is called by us from mytest_handler to check for TSHttpTxnNextHopIPGet
3153 static int
3154 checkHttpTxnNextHopIPGet(SocketTest *test, void *data)
3155 {
3156   TSHttpTxn txnp      = static_cast<TSHttpTxn>(data);
3157   in_addr_t actual_ip = htonl(INADDR_LOOPBACK); /* 127.0.0.1 is expected because the client is on the same machine */
3158   sockaddr const *ptr;
3159   in_addr_t nexthopip;
3160 
3161   ptr = TSHttpTxnNextHopAddrGet(txnp);
3162   if (ptr == nullptr || (nexthopip = ats_ip4_addr_cast(ptr)) == 0) {
3163     test->test_next_hop_ip_get = false;
3164     SDK_RPRINT(test->regtest, "TSHttpTxnNextHopIPGet", "TestCase1", TC_FAIL, "TSHttpTxnNextHopIPGet returns 0 %s",
3165                ptr ? "address" : "pointer");
3166     return TS_EVENT_CONTINUE;
3167   }
3168 
3169   if (nexthopip == actual_ip) {
3170     test->test_next_hop_ip_get = true;
3171     SDK_RPRINT(test->regtest, "TSHttpTxnNextHopIPGet", "TestCase1", TC_PASS, "ok");
3172   } else {
3173     test->test_next_hop_ip_get = false;
3174     SDK_RPRINT(test->regtest, "TSHttpTxnNextHopIPGet", "TestCase1", TC_FAIL, "Value's Mismatch [expected %0.8x got %0.8x]",
3175                actual_ip, nexthopip);
3176   }
3177 
3178   return TS_EVENT_CONTINUE;
3179 }
3180 
3181 // This func is called by us from mytest_handler to test TSHttpTxnServerIPGet
3182 static int
3183 checkHttpTxnServerIPGet(SocketTest *test, void *data)
3184 {
3185   sockaddr const *ptr;
3186   in_addr_t ip;
3187   TSHttpTxn txnp      = static_cast<TSHttpTxn>(data);
3188   in_addr_t actual_ip = htonl(INADDR_LOOPBACK); /* 127.0.0.1 is expected because the client is on the same machine */
3189 
3190   ptr = TSHttpTxnServerAddrGet(txnp);
3191   if (nullptr == ptr || 0 == (ip = ats_ip4_addr_cast(ptr))) {
3192     test->test_server_ip_get = false;
3193     SDK_RPRINT(test->regtest, "TSHttpTxnServerIPGet", "TestCase1", TC_FAIL, "TSHttpTxnServerIPGet returns 0 %s",
3194                ptr ? "address" : "pointer");
3195     return TS_EVENT_CONTINUE;
3196   }
3197 
3198   if (ip == actual_ip) {
3199     test->test_server_ip_get = true;
3200     SDK_RPRINT(test->regtest, "TSHttpTxnServerIPGet", "TestCase1", TC_PASS, "ok");
3201   } else {
3202     test->test_server_ip_get = false;
3203     SDK_RPRINT(test->regtest, "TSHttpTxnServerIPGet", "TestCase1", TC_FAIL, "Value's Mismatch");
3204   }
3205 
3206   return TS_EVENT_CONTINUE;
3207 }
3208 
3209 // This func is called by us from mytest_handler to test TSHttpTxnIncomingAddrGet
3210 static int
3211 checkHttpTxnIncomingAddrGet(SocketTest *test, void *data)
3212 {
3213   uint16_t port;
3214   const HttpProxyPort *proxy_port = HttpProxyPort::findHttp(AF_INET);
3215   TSHttpTxn txnp                  = static_cast<TSHttpTxn>(data);
3216   sockaddr const *ptr             = TSHttpTxnIncomingAddrGet(txnp);
3217 
3218   if (nullptr == proxy_port) {
3219     SDK_RPRINT(test->regtest, "TSHttpTxnIncomingPortGet", "TestCase1", TC_FAIL,
3220                "TSHttpTxnIncomingAddrGet failed to find configured HTTP port.");
3221     test->test_client_incoming_port_get = false;
3222     return TS_EVENT_CONTINUE;
3223   }
3224   if (nullptr == ptr) {
3225     SDK_RPRINT(test->regtest, "TSHttpTxnIncomingPortGet", "TestCase1", TC_FAIL, "TSHttpTxnIncomingAddrGet returns 0 pointer");
3226     test->test_client_incoming_port_get = false;
3227     return TS_EVENT_CONTINUE;
3228   }
3229   port = ats_ip_port_host_order(ptr);
3230 
3231   TSDebug(UTDBG_TAG, "TS HTTP port = %x, Txn incoming client port %x", proxy_port->m_port, port);
3232 
3233   if (port == proxy_port->m_port) {
3234     SDK_RPRINT(test->regtest, "TSHttpTxnIncomingAddrGet", "TestCase1", TC_PASS, "ok");
3235     test->test_client_incoming_port_get = true;
3236   } else {
3237     SDK_RPRINT(test->regtest, "TSHttpTxnIncomingAddrGet", "TestCase1", TC_FAIL,
3238                "Value's Mismatch. From Function: %d  Expected value: %d", port, proxy_port->m_port);
3239     test->test_client_incoming_port_get = false;
3240   }
3241   return TS_EVENT_CONTINUE;
3242 }
3243 
3244 // This func is called by us from mytest_handler to test TSHttpTxnClientAddrGet
3245 static int
3246 checkHttpTxnClientAddrGet(SocketTest *test, void *data)
3247 {
3248   uint16_t port;
3249   uint16_t browser_port;
3250   TSHttpTxn txnp      = static_cast<TSHttpTxn>(data);
3251   sockaddr const *ptr = TSHttpTxnClientAddrGet(txnp);
3252 
3253   browser_port = test->browser->local_port;
3254 
3255   if (nullptr == ptr) {
3256     SDK_RPRINT(test->