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 typedef int (*TxnHandler)(TSCont contp, TSEvent event, void *data);
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 = (char *)strstr((const char *)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 = (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 = (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 = (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 = (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((char *)(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 = (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((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((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 = (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 = (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((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((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 = (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      = (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((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 = (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  = (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 = (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((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 = (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 = (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 = (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 = (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 = (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 = (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 = (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((char *)(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 = (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 = (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 = (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     = (TSVConn)data;
1407     TSVConnRead((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 = (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((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 = (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 = (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((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 = (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((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 ((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 ((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 ((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 ((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 = (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 = (stat_buffer_input.st_size <= (off_t)sizeof(input_buffer)) ? (stat_buffer_input.st_size) : (sizeof(input_buffer));
2089 
2090   // TSfgets
2091   if ((ret_val = TSfgets(source_read_file, input_buffer, read_amount)) == nullptr) {
2092     SDK_RPRINT(test, "TSfgets", "TestCase1", TC_FAIL, "can't read from file");
2093 
2094     // no need to continue, return
2095     *pstatus = REGRESSION_TEST_FAILED;
2096     if (source_read_file != nullptr) {
2097       TSfclose(source_read_file);
2098     }
2099     if (write_file != nullptr) {
2100       TSfclose(write_file);
2101     }
2102     return;
2103   } else {
2104     if (ret_val != input_buffer) {
2105       SDK_RPRINT(test, "TSfgets", "TestCase2", TC_FAIL, "reading error");
2106 
2107       // no need to continue, return
2108       *pstatus = REGRESSION_TEST_FAILED;
2109       if (source_read_file != nullptr) {
2110         TSfclose(source_read_file);
2111       }
2112       if (write_file != nullptr) {
2113         TSfclose(write_file);
2114       }
2115       return;
2116     } else {
2117       SDK_RPRINT(test, "TSfgets", "TestCase1", TC_PASS, "ok");
2118     }
2119   }
2120 
2121   // TSfwrite
2122   wrote = TSfwrite(write_file, input_buffer, read_amount);
2123   if (wrote != read_amount) {
2124     SDK_RPRINT(test, "TSfwrite", "TestCase1", TC_FAIL, "writing error");
2125 
2126     // no need to continue, return
2127     *pstatus = REGRESSION_TEST_FAILED;
2128     if (source_read_file != nullptr) {
2129       TSfclose(source_read_file);
2130     }
2131     if (write_file != nullptr) {
2132       TSfclose(write_file);
2133     }
2134     return;
2135   }
2136 
2137   SDK_RPRINT(test, "TSfwrite", "TestCase1", TC_PASS, "ok");
2138 
2139   // TSfflush
2140   if (stat(write_file_name, &stat_buffer_pre) != 0) {
2141     SDK_RPRINT(test, "stat", "std func", TC_FAIL, "TSfwrite error");
2142 
2143     // no need to continue, return
2144     *pstatus = REGRESSION_TEST_FAILED;
2145     if (source_read_file != nullptr) {
2146       TSfclose(source_read_file);
2147     }
2148     if (write_file != nullptr) {
2149       TSfclose(write_file);
2150     }
2151     return;
2152   }
2153 
2154   TSfflush(write_file); // write_file should point to write_file_name
2155 
2156   if (stat(write_file_name, &stat_buffer_post) != 0) {
2157     SDK_RPRINT(test, "stat", "std func", TC_FAIL, "TSfflush error");
2158 
2159     // no need to continue, return
2160     *pstatus = REGRESSION_TEST_FAILED;
2161     if (source_read_file != nullptr) {
2162       TSfclose(source_read_file);
2163     }
2164     if (write_file != nullptr) {
2165       TSfclose(write_file);
2166     }
2167     return;
2168   }
2169 
2170   if ((stat_buffer_pre.st_size == 0) && (stat_buffer_post.st_size == read_amount)) {
2171     SDK_RPRINT(test, "TSfflush", "TestCase1", TC_PASS, "ok");
2172   } else {
2173     SDK_RPRINT(test, "TSfflush", "TestCase1", TC_FAIL, "TSfflush error");
2174 
2175     // no need to continue, return
2176     *pstatus = REGRESSION_TEST_FAILED;
2177     if (source_read_file != nullptr) {
2178       TSfclose(source_read_file);
2179     }
2180     if (write_file != nullptr) {
2181       TSfclose(write_file);
2182     }
2183     return;
2184   }
2185 
2186   // TSfread
2187   // open again for reading
2188   cmp_read_file = TSfopen(write_file_name, "r");
2189   if (cmp_read_file == nullptr) {
2190     SDK_RPRINT(test, "TSfopen", "TestCase3", TC_FAIL, "can't open file for reading");
2191 
2192     // no need to continue, return
2193     *pstatus = REGRESSION_TEST_FAILED;
2194     if (source_read_file != nullptr) {
2195       TSfclose(source_read_file);
2196     }
2197     if (write_file != nullptr) {
2198       TSfclose(write_file);
2199     }
2200     return;
2201   }
2202 
2203   read_amount = (stat_buffer_input.st_size <= (off_t)sizeof(cmp_buffer)) ? (stat_buffer_input.st_size) : (sizeof(cmp_buffer));
2204 
2205   // TSfread on read file
2206   read = TSfread(cmp_read_file, cmp_buffer, read_amount);
2207   if (read != read_amount) {
2208     SDK_RPRINT(test, "TSfread", "TestCase1", TC_FAIL, "can't reading");
2209 
2210     // no need to continue, return
2211     *pstatus = REGRESSION_TEST_FAILED;
2212     if (source_read_file != nullptr) {
2213       TSfclose(source_read_file);
2214     }
2215     if (write_file != nullptr) {
2216       TSfclose(write_file);
2217     }
2218     if (cmp_read_file != nullptr) {
2219       TSfclose(cmp_read_file);
2220     }
2221     return;
2222   } else {
2223     SDK_RPRINT(test, "TSfread", "TestCase1", TC_PASS, "ok");
2224   }
2225 
2226   // compare input_buffer and cmp_buffer buffers
2227   if (memcmp(input_buffer, cmp_buffer, read_amount) != 0) {
2228     SDK_RPRINT(test, "TSfread", "TestCase2", TC_FAIL, "reading error");
2229 
2230     // no need to continue, return
2231     *pstatus = REGRESSION_TEST_FAILED;
2232     if (source_read_file != nullptr) {
2233       TSfclose(source_read_file);
2234     }
2235     if (write_file != nullptr) {
2236       TSfclose(write_file);
2237     }
2238     if (cmp_read_file != nullptr) {
2239       TSfclose(cmp_read_file);
2240     }
2241     return;
2242   } else {
2243     SDK_RPRINT(test, "TSfread", "TestCase2", TC_PASS, "ok");
2244   }
2245 
2246   // remove the tmp file
2247   if (unlink(write_file_name) != 0) {
2248     SDK_RPRINT(test, "unlink", "std func", TC_FAIL, "can't remove temp file");
2249   }
2250   // TSfclose on read file
2251   TSfclose(source_read_file);
2252   SDK_RPRINT(test, "TSfclose", "TestCase1", TC_PASS, "ok");
2253 
2254   // TSfclose on write file
2255   TSfclose(write_file);
2256   SDK_RPRINT(test, "TSfclose", "TestCase2", TC_PASS, "ok");
2257 
2258   *pstatus = REGRESSION_TEST_PASSED;
2259   if (cmp_read_file != nullptr) {
2260     TSfclose(cmp_read_file);
2261   }
2262 }
2263 
2264 /* TSThread */
2265 
2266 //////////////////////////////////////////////
2267 //       SDK_API_TSThread
2268 //
2269 // Unit Test for API: TSThread
2270 //                    TSThreadCreate
2271 //                    TSThreadSelf
2272 //////////////////////////////////////////////
2273 static int thread_err_count = 0;
2274 static RegressionTest *SDK_Thread_test;
2275 static int *SDK_Thread_pstatus;
2276 static void *thread_create_handler(void *arg);
2277 
2278 static void *
2279 thread_create_handler(void * /* arg ATS_UNUSED */)
2280 {
2281   TSThread athread;
2282   // Fix me: do more useful work
2283   sleep(10);
2284 
2285   athread = TSThreadSelf();
2286   if (athread == nullptr) {
2287     thread_err_count++;
2288     SDK_RPRINT(SDK_Thread_test, "TSThreadCreate", "TestCase2", TC_FAIL, "can't get thread");
2289   } else {
2290     SDK_RPRINT(SDK_Thread_test, "TSThreadCreate", "TestCase2", TC_PASS, "ok");
2291   }
2292 
2293   if (thread_err_count > 0) {
2294     *SDK_Thread_pstatus = REGRESSION_TEST_FAILED;
2295   } else {
2296     *SDK_Thread_pstatus = REGRESSION_TEST_PASSED;
2297   }
2298 
2299   return nullptr;
2300 }
2301 
2302 // Fix me: Solaris threads/Win2K threads tests
2303 
2304 // Argument data passed to thread init functions
2305 //  cannot be allocated on the stack.
2306 
2307 REGRESSION_TEST(SDK_API_TSThread)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2308 {
2309   *pstatus           = REGRESSION_TEST_INPROGRESS;
2310   SDK_Thread_test    = test;
2311   SDK_Thread_pstatus = pstatus;
2312 
2313   TSThread curr_thread = nullptr;
2314   //    TSThread created_thread = 0;
2315   pthread_t curr_tid;
2316 
2317   curr_tid = pthread_self();
2318 
2319   // TSThreadSelf
2320   curr_thread = TSThreadSelf();
2321   if (curr_thread == nullptr) {
2322     SDK_RPRINT(test, "TSThreadSelf", "TestCase1", TC_FAIL, "can't get the current thread");
2323     thread_err_count++;
2324   } else {
2325     SDK_RPRINT(test, "TSThreadSelf", "TestCase1", TC_PASS, "ok");
2326   }
2327 
2328   // TSThreadCreate
2329   TSThread created_thread = TSThreadCreate(thread_create_handler, (void *)(intptr_t)curr_tid);
2330   if (created_thread == nullptr) {
2331     thread_err_count++;
2332     SDK_RPRINT(test, "TSThreadCreate", "TestCase1", TC_FAIL, "can't create thread");
2333   } else {
2334     SDK_RPRINT(test, "TSThreadCreate", "TestCase1", TC_PASS, "ok");
2335   }
2336 
2337   if (created_thread != nullptr) {
2338     TSThreadWait(created_thread);
2339     TSThreadDestroy(created_thread);
2340   }
2341 }
2342 
2343 //////////////////////////////////////////////
2344 //       SDK_API_TSThread
2345 //
2346 // Unit Test for API: TSThreadInit
2347 //                    TSThreadDestroy
2348 //////////////////////////////////////////////
2349 static int thread_init_err_count = 0;
2350 static RegressionTest *SDK_ThreadInit_test;
2351 static int *SDK_ThreadInit_pstatus;
2352 static void *pthread_start_func(void *arg);
2353 
2354 static void *
2355 pthread_start_func(void * /* arg ATS_UNUSED */)
2356 {
2357   TSThread temp_thread = nullptr;
2358 
2359   // TSThreadInit
2360   temp_thread = TSThreadInit();
2361 
2362   if (!temp_thread) {
2363     SDK_RPRINT(SDK_ThreadInit_test, "TSThreadInit", "TestCase2", TC_FAIL, "can't init thread");
2364     thread_init_err_count++;
2365   } else {
2366     SDK_RPRINT(SDK_ThreadInit_test, "TSThreadInit", "TestCase2", TC_PASS, "ok");
2367   }
2368 
2369   // Clean up this thread
2370   if (temp_thread) {
2371     TSThreadDestroy(temp_thread);
2372   }
2373 
2374   if (thread_init_err_count > 0) {
2375     *SDK_ThreadInit_pstatus = REGRESSION_TEST_FAILED;
2376   } else {
2377     *SDK_ThreadInit_pstatus = REGRESSION_TEST_PASSED;
2378   }
2379 
2380   return nullptr;
2381 }
2382 
2383 REGRESSION_TEST(SDK_API_TSThreadInit)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2384 {
2385   *pstatus               = REGRESSION_TEST_INPROGRESS;
2386   SDK_ThreadInit_test    = test;
2387   SDK_ThreadInit_pstatus = pstatus;
2388 
2389   pthread_t curr_tid, new_tid;
2390 
2391   curr_tid = pthread_self();
2392 
2393   int ret;
2394   errno = 0;
2395   ret   = pthread_create(&new_tid, nullptr, pthread_start_func, (void *)(intptr_t)curr_tid);
2396   if (ret != 0) {
2397     thread_init_err_count++;
2398     SDK_RPRINT(test, "TSThreadInit", "TestCase1", TC_FAIL, "can't create pthread");
2399   } else {
2400     SDK_RPRINT(test, "TSThreadInit", "TestCase1", TC_PASS, "ok");
2401   }
2402 }
2403 
2404 /* Action */
2405 
2406 //////////////////////////////////////////////
2407 //       SDK_API_TSAction
2408 //
2409 // Unit Test for API: TSActionCancel
2410 //////////////////////////////////////////////
2411 
2412 static RegressionTest *SDK_ActionCancel_test;
2413 static int *SDK_ActionCancel_pstatus;
2414 
2415 int
2416 action_cancel_handler(TSCont contp, TSEvent event, void * /* edata ATS_UNUSED */)
2417 {
2418   if (event == TS_EVENT_IMMEDIATE) { // called from schedule_imm OK
2419     SDK_RPRINT(SDK_ActionCancel_test, "TSActionCancel", "TestCase1", TC_PASS, "ok");
2420     *SDK_ActionCancel_pstatus = REGRESSION_TEST_PASSED;
2421   } else if (event == TS_EVENT_TIMEOUT) { // called from schedule_in Not OK.
2422     SDK_RPRINT(SDK_ActionCancel_test, "TSActionCancel", "TestCase1", TC_FAIL, "bad action");
2423     *SDK_ActionCancel_pstatus = REGRESSION_TEST_FAILED;
2424   } else { // there is sth wrong
2425     SDK_RPRINT(SDK_ActionCancel_test, "TSActionCancel", "TestCase1", TC_FAIL, "bad event");
2426     *SDK_ActionCancel_pstatus = REGRESSION_TEST_FAILED;
2427   }
2428 
2429   TSContDestroy(contp);
2430   return 0;
2431 }
2432 
2433 REGRESSION_TEST(SDK_API_TSActionCancel)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2434 {
2435   *pstatus = REGRESSION_TEST_INPROGRESS;
2436 
2437   SDK_ActionCancel_test    = test;
2438   SDK_ActionCancel_pstatus = pstatus;
2439 
2440   TSMutex cont_mutex = TSMutexCreate();
2441   TSCont contp       = TSContCreate(action_cancel_handler, cont_mutex);
2442   TSAction actionp   = TSContScheduleOnPool(contp, 10000, TS_THREAD_POOL_NET);
2443 
2444   TSMutexLock(cont_mutex);
2445   if (TSActionDone(actionp)) {
2446     *pstatus = REGRESSION_TEST_FAILED;
2447     TSMutexUnlock(cont_mutex);
2448     return;
2449   } else {
2450     TSActionCancel(actionp);
2451   }
2452   TSMutexUnlock(cont_mutex);
2453 
2454   TSContScheduleOnPool(contp, 0, TS_THREAD_POOL_NET);
2455 }
2456 
2457 //////////////////////////////////////////////
2458 //       SDK_API_TSAction
2459 //
2460 // Unit Test for API: TSActionDone
2461 //////////////////////////////////////////////
2462 /* Currently, don't know how to test it because TSAction
2463    is at "done" status only "shortly" after finish
2464    executing action_done_handler. Another possibility is
2465    to use reentrant call. But in both cases it's not
2466    guaranteed to get ActionDone.
2467    */
2468 
2469 /* Continuations */
2470 
2471 //////////////////////////////////////////////
2472 //       SDK_API_TSCont
2473 //
2474 // Unit Test for API: TSContCreate
2475 //                    TSContCall
2476 //////////////////////////////////////////////
2477 
2478 // this is needed for asynchronous APIs
2479 static RegressionTest *SDK_ContCreate_test;
2480 static int *SDK_ContCreate_pstatus;
2481 
2482 int
2483 cont_handler(TSCont /* contp ATS_UNUSED */, TSEvent /* event ATS_UNUSED */, void * /* edata ATS_UNUSED */)
2484 {
2485   SDK_RPRINT(SDK_ContCreate_test, "TSContCreate", "TestCase1", TC_PASS, "ok");
2486   SDK_RPRINT(SDK_ContCreate_test, "TSContCall", "TestCase1", TC_PASS, "ok");
2487 
2488   *SDK_ContCreate_pstatus = REGRESSION_TEST_PASSED;
2489 
2490   return 0;
2491 }
2492 
2493 REGRESSION_TEST(SDK_API_TSContCreate)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2494 {
2495   *pstatus = REGRESSION_TEST_INPROGRESS;
2496 
2497   // For asynchronous APIs, use static vars to store test and pstatus
2498   SDK_ContCreate_test    = test;
2499   SDK_ContCreate_pstatus = pstatus;
2500 
2501   TSMutex mutexp = TSMutexCreate();
2502   TSCont contp   = TSContCreate(cont_handler, mutexp);
2503 
2504   if (TS_SUCCESS == TSMutexLockTry(mutexp)) { // Mutex is grabbed successfully
2505     TSContCall(contp, (TSEvent)0, nullptr);
2506     TSMutexUnlock(mutexp);
2507   } else { // mutex has problems
2508     SDK_RPRINT(SDK_ContCreate_test, "TSContCreate", "TestCase1", TC_FAIL, "continuation creation has problems");
2509     SDK_RPRINT(SDK_ContCreate_test, "TSContCall", "TestCase1", TC_FAIL, "continuation has problems");
2510 
2511     *pstatus = REGRESSION_TEST_FAILED;
2512   }
2513 
2514   TSContDestroy(contp);
2515 }
2516 
2517 //////////////////////////////////////////////
2518 //       SDK_API_TSCont
2519 //
2520 // Unit Test for API: TSContDataGet
2521 //                    TSContDataSet
2522 //////////////////////////////////////////////
2523 
2524 // this is needed for asynchronous APIs
2525 static RegressionTest *SDK_ContData_test;
2526 static int *SDK_ContData_pstatus;
2527 
2528 // this is specific for this test
2529 typedef struct {
2530   int data1;
2531   int data2;
2532 } MyData;
2533 
2534 int
2535 cont_data_handler(TSCont contp, TSEvent /* event ATS_UNUSED */, void * /* edata ATS_UNUSED */)
2536 {
2537   MyData *my_data = (MyData *)TSContDataGet(contp);
2538 
2539   if (my_data->data1 == 1 && my_data->data2 == 2) {
2540     SDK_RPRINT(SDK_ContData_test, "TSContDataSet", "TestCase1", TC_PASS, "ok");
2541     SDK_RPRINT(SDK_ContData_test, "TSContDataGet", "TestCase1", TC_PASS, "ok");
2542 
2543     *SDK_ContData_pstatus = REGRESSION_TEST_PASSED;
2544   } else {
2545     // If we get bad data, it's a failure
2546     SDK_RPRINT(SDK_ContData_test, "TSContDataSet", "TestCase1", TC_FAIL, "bad data");
2547     SDK_RPRINT(SDK_ContData_test, "TSContDataGet", "TestCase1", TC_FAIL, "bad data");
2548 
2549     *SDK_ContData_pstatus = REGRESSION_TEST_FAILED;
2550   }
2551 
2552   TSfree(my_data);
2553   TSContDestroy(contp);
2554   return 0;
2555 }
2556 
2557 REGRESSION_TEST(SDK_API_TSContDataGet)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2558 {
2559   *pstatus = REGRESSION_TEST_INPROGRESS;
2560 
2561   // For asynchronous APIs, use static vars to store test and pstatus
2562   SDK_ContData_test    = test;
2563   SDK_ContData_pstatus = pstatus;
2564 
2565   TSCont contp = TSContCreate(cont_data_handler, TSMutexCreate());
2566 
2567   MyData *my_data = (MyData *)TSmalloc(sizeof(MyData));
2568   my_data->data1  = 1;
2569   my_data->data2  = 2;
2570 
2571   TSContDataSet(contp, (void *)my_data);
2572 
2573   TSContScheduleOnPool(contp, 0, TS_THREAD_POOL_NET);
2574 }
2575 
2576 //////////////////////////////////////////////
2577 //       SDK_API_TSCont
2578 //
2579 // Unit Test for API: TSContMutexGet
2580 //////////////////////////////////////////////
2581 
2582 REGRESSION_TEST(SDK_API_TSContMutexGet)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2583 {
2584   bool test_passed = false;
2585   *pstatus         = REGRESSION_TEST_INPROGRESS;
2586 
2587   TSMutex mutexp_input;
2588   TSMutex mutexp_output;
2589   TSCont contp;
2590 
2591   mutexp_input = TSMutexCreate();
2592   contp        = TSContCreate(cont_handler, mutexp_input);
2593 
2594   mutexp_output = TSContMutexGet(contp);
2595 
2596   if (mutexp_input == mutexp_output) {
2597     SDK_RPRINT(test, "TSContMutexGet", "TestCase1", TC_PASS, "ok");
2598     test_passed = true;
2599   } else {
2600     SDK_RPRINT(test, "TSContMutexGet", "TestCase1", TC_FAIL, "Continuation's mutex corrupted");
2601   }
2602 
2603   // Status of the whole test
2604   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2605 
2606   TSContDestroy(contp);
2607 }
2608 
2609 //////////////////////////////////////////////
2610 //       SDK_API_TSCont
2611 //
2612 // Unit Test for API: TSContScheduleOnPool
2613 //////////////////////////////////////////////
2614 
2615 // this is needed for asynchronous APIs
2616 static RegressionTest *SDK_ContSchedule_test;
2617 static int *SDK_ContSchedule_pstatus;
2618 
2619 // this is specific for this test
2620 static int tc1_count = 0;
2621 static int tc2_count = 0;
2622 
2623 int
2624 cont_schedule_handler(TSCont contp, TSEvent event, void * /* edata ATS_UNUSED */)
2625 {
2626   if (event == TS_EVENT_IMMEDIATE) {
2627     // Test Case 1
2628     SDK_RPRINT(SDK_ContSchedule_test, "TSContScheduleOnPool", "TestCase1", TC_PASS, "ok");
2629     tc1_count++;
2630   } else if (event == TS_EVENT_TIMEOUT) {
2631     // Test Case 2
2632     SDK_RPRINT(SDK_ContSchedule_test, "TSContScheduleOnPool", "TestCase2", TC_PASS, "ok");
2633     tc2_count++;
2634   } else {
2635     // If we receive a bad event, it's a failure
2636     SDK_RPRINT(SDK_ContSchedule_test, "TSContScheduleOnPool", "TestCase1|2", TC_FAIL, "received unexpected event number %d", event);
2637     *SDK_ContSchedule_pstatus = REGRESSION_TEST_FAILED;
2638     return 0;
2639   }
2640 
2641   // We expect to be called once for TC1 and once for TC2
2642   if ((tc1_count == 1) && (tc2_count == 1)) {
2643     *SDK_ContSchedule_pstatus = REGRESSION_TEST_PASSED;
2644   }
2645   // If TC1 or TC2 executed more than once, something is fishy..
2646   else if (tc1_count + tc2_count >= 2) {
2647     *SDK_ContSchedule_pstatus = REGRESSION_TEST_FAILED;
2648   }
2649 
2650   TSContDestroy(contp);
2651   return 0;
2652 }
2653 
2654 /* Mutex */
2655 
2656 /*
2657    Fix me: test for grabbing the mutex from two
2658    different threads.
2659    */
2660 
2661 //////////////////////////////////////////////
2662 //       SDK_API_TSMutex
2663 //
2664 // Unit Test for API: TSMutexCreate
2665 //                    TSMutexLock
2666 //                    TSMutexUnLock
2667 //////////////////////////////////////////////
2668 
2669 REGRESSION_TEST(SDK_API_TSMutexCreate)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2670 {
2671   bool test_passed = false;
2672   *pstatus         = REGRESSION_TEST_INPROGRESS;
2673 
2674   TSMutex mutexp = TSMutexCreate();
2675 
2676   TSMutexLock(mutexp);
2677 
2678   /* This is normal because all locking is from the same thread */
2679   TSReturnCode lock1 = TS_ERROR;
2680   TSReturnCode lock2 = TS_ERROR;
2681 
2682   lock1 = TSMutexLockTry(mutexp);
2683   lock2 = TSMutexLockTry(mutexp);
2684 
2685   if (TS_SUCCESS == lock1 && TS_SUCCESS == lock2) {
2686     SDK_RPRINT(test, "TSMutexCreate", "TestCase1", TC_PASS, "ok");
2687     SDK_RPRINT(test, "TSMutexLock", "TestCase1", TC_PASS, "ok");
2688     SDK_RPRINT(test, "TSMutexLockTry", "TestCase1", TC_PASS, "ok");
2689     test_passed = true;
2690   } else {
2691     SDK_RPRINT(test, "TSMutexCreate", "TestCase1", TC_FAIL, "mutex can't be grabbed twice from the same thread");
2692     SDK_RPRINT(test, "TSMutexLock", "TestCase1", TC_FAIL, "mutex can't be grabbed twice from the same thread");
2693     SDK_RPRINT(test, "TSMutexLockTry", "TestCase1", TC_FAIL, "mutex can't be grabbed twice from the same thread");
2694   }
2695 
2696   TSMutexUnlock(mutexp);
2697   SDK_RPRINT(test, "TSMutexUnLock", "TestCase1", TC_PASS, "ok");
2698 
2699   if (test_passed) {
2700     *pstatus = REGRESSION_TEST_PASSED;
2701   } else {
2702     *pstatus = REGRESSION_TEST_FAILED;
2703   }
2704 }
2705 
2706 /* IOBuffer */
2707 
2708 //////////////////////////////////////////////
2709 //       SDK_API_TSIOBuffer
2710 //
2711 // Unit Test for API: TSIOBufferCreate
2712 //                    TSIOBufferWaterMarkGet
2713 //                    TSIOBufferWaterMarkSet
2714 //////////////////////////////////////////////
2715 
2716 REGRESSION_TEST(SDK_API_TSIOBufferCreate)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2717 {
2718   bool test_passed = false;
2719   *pstatus         = REGRESSION_TEST_INPROGRESS;
2720 
2721   int64_t watermark = 1000;
2722 
2723   TSIOBuffer bufp = TSIOBufferCreate();
2724 
2725   TSIOBufferWaterMarkSet(bufp, watermark);
2726   watermark = TSIOBufferWaterMarkGet(bufp);
2727 
2728   if (watermark == 1000) {
2729     SDK_RPRINT(test, "TSIOBufferCreate", "TestCase1", TC_PASS, "ok");
2730     SDK_RPRINT(test, "TSIOBufferWaterMarkGet", "TestCase1", TC_PASS, "ok");
2731     SDK_RPRINT(test, "TSIOBufferWaterMarkSet", "TestCase1", TC_PASS, "ok");
2732     test_passed = true;
2733   } else {
2734     SDK_RPRINT(test, "TSIOBufferCreate", "TestCase1", TC_FAIL, "watermark failed");
2735     SDK_RPRINT(test, "TSIOBufferWaterMarkGet", "TestCase1", TC_FAIL, "watermark failed");
2736     SDK_RPRINT(test, "TSIOBufferWaterMarkSet", "TestCase1", TC_FAIL, "watermark failed");
2737   }
2738 
2739   TSIOBufferDestroy(bufp);
2740 
2741   // Status of the whole test
2742   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2743   return;
2744 }
2745 
2746 //////////////////////////////////////////////
2747 //       SDK_API_TSIOBuffer
2748 //
2749 // Unit Test for API: TSIOBufferSizedCreate
2750 //                    TSIOBufferProduce
2751 //                    TSIOBufferReaderAlloc
2752 //                    TSIOBufferReaderAvail
2753 //////////////////////////////////////////////
2754 
2755 REGRESSION_TEST(SDK_API_TSIOBufferProduce)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2756 {
2757   bool test_passed = false;
2758   *pstatus         = REGRESSION_TEST_INPROGRESS;
2759 
2760   TSIOBuffer bufp = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_4K); // size is 4096
2761 
2762   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2763 
2764   TSIOBufferProduce(bufp, 10);
2765 
2766   int64_t reader_avail = TSIOBufferReaderAvail(readerp);
2767   if (reader_avail == 10) {
2768     SDK_RPRINT(test, "TSIOBufferProduce", "TestCase1", TC_PASS, "ok");
2769     SDK_RPRINT(test, "TSIOBufferReaderAlloc", "TestCase1", TC_PASS, "ok");
2770     SDK_RPRINT(test, "TSIOBufferReaderAvail", "TestCase1", TC_PASS, "ok");
2771     test_passed = true;
2772   } else {
2773     SDK_RPRINT(test, "TSIOBufferProduce", "TestCase1", TC_FAIL, "failed");
2774     SDK_RPRINT(test, "TSIOBufferReaderAlloc", "TestCase1", TC_FAIL, "failed");
2775     SDK_RPRINT(test, "TSIOBufferReaderAvail", "TestCase1", TC_FAIL, "failed");
2776   }
2777 
2778   // Status of the whole test
2779   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2780   return;
2781 }
2782 
2783 //////////////////////////////////////////////
2784 //       SDK_API_TSIOBuffer
2785 //
2786 // Unit Test for API: TSIOBufferReaderConsume
2787 //////////////////////////////////////////////
2788 
2789 REGRESSION_TEST(SDK_API_TSIOBufferReaderConsume)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2790 {
2791   bool test_passed = false;
2792   *pstatus         = REGRESSION_TEST_INPROGRESS;
2793 
2794   TSIOBuffer bufp = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_4K);
2795 
2796   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2797 
2798   TSIOBufferProduce(bufp, 10);
2799   TSIOBufferReaderConsume(readerp, 10);
2800 
2801   int64_t reader_avail = TSIOBufferReaderAvail(readerp);
2802   if (reader_avail == 0) {
2803     SDK_RPRINT(test, "TSIOBufferReaderConsume", "TestCase1", TC_PASS, "ok");
2804     test_passed = true;
2805   } else {
2806     SDK_RPRINT(test, "TSIOBufferReaderConsume", "TestCase1", TC_FAIL, "failed");
2807   }
2808 
2809   // Status of the whole test
2810   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2811   return;
2812 }
2813 
2814 //////////////////////////////////////////////
2815 //       SDK_API_TSIOBuffer
2816 //
2817 // Unit Test for API: TSIOBufferReaderClone
2818 //////////////////////////////////////////////
2819 
2820 REGRESSION_TEST(SDK_API_TSIOBufferReaderClone)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2821 {
2822   bool test_passed = false;
2823   *pstatus         = REGRESSION_TEST_INPROGRESS;
2824 
2825   TSIOBuffer bufp          = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_4K);
2826   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2827 
2828   TSIOBufferProduce(bufp, 10);
2829   TSIOBufferReaderConsume(readerp, 5);
2830 
2831   TSIOBufferReader readerp2 = TSIOBufferReaderClone(readerp);
2832 
2833   int64_t reader_avail = TSIOBufferReaderAvail(readerp2);
2834   if (reader_avail == 5) {
2835     SDK_RPRINT(test, "TSIOBufferReaderClone", "TestCase1", TC_PASS, "ok");
2836     test_passed = true;
2837   } else {
2838     SDK_RPRINT(test, "TSIOBufferReaderClone", "TestCase1", TC_FAIL, "failed");
2839   }
2840 
2841   // Status of the whole test
2842   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2843   return;
2844 }
2845 
2846 //////////////////////////////////////////////
2847 //       SDK_API_TSIOBuffer
2848 //
2849 // Unit Test for API: TSIOBufferStart
2850 //                    TSIOBufferReaderStart
2851 //////////////////////////////////////////////
2852 
2853 REGRESSION_TEST(SDK_API_TSIOBufferStart)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2854 {
2855   bool test_passed = false;
2856   *pstatus         = REGRESSION_TEST_INPROGRESS;
2857 
2858   TSIOBuffer bufp = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_4K);
2859 
2860   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2861 
2862   if (TSIOBufferStart(bufp) == TSIOBufferReaderStart(readerp)) {
2863     SDK_RPRINT(test, "TSIOBufferStart", "TestCase1", TC_PASS, "ok");
2864     SDK_RPRINT(test, "TSIOBufferReaderStart", "TestCase1", TC_PASS, "ok");
2865     test_passed = true;
2866   } else {
2867     SDK_RPRINT(test, "TSIOBufferStart", "TestCase1", TC_FAIL, "failed");
2868     SDK_RPRINT(test, "TSIOBufferReaderStart", "TestCase1", TC_FAIL, "failed");
2869   }
2870 
2871   // Status of the whole test
2872   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2873   return;
2874 }
2875 
2876 //////////////////////////////////////////////
2877 //       SDK_API_TSIOBuffer
2878 //
2879 // Unit Test for API: TSIOBufferCopy
2880 //                    TSIOBufferWrite
2881 //                    TSIOBufferReaderCopy
2882 //////////////////////////////////////////////
2883 
2884 REGRESSION_TEST(SDK_API_TSIOBufferCopy)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2885 {
2886   bool test_passed = false;
2887   *pstatus         = REGRESSION_TEST_INPROGRESS;
2888 
2889   char input_buf[] = "This is the test for TSIOBufferCopy, TSIOBufferWrite, TSIOBufferReaderCopy";
2890   char output_buf[1024];
2891   TSIOBuffer bufp  = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_4K);
2892   TSIOBuffer bufp2 = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_4K);
2893 
2894   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2895   TSIOBufferWrite(bufp, input_buf, (strlen(input_buf) + 1));
2896   TSIOBufferCopy(bufp2, readerp, (strlen(input_buf) + 1), 0);
2897   TSIOBufferReaderCopy(readerp, output_buf, (strlen(input_buf) + 1));
2898 
2899   if (strcmp(input_buf, output_buf) == 0) {
2900     SDK_RPRINT(test, "TSIOBufferWrite", "TestCase1", TC_PASS, "ok");
2901     SDK_RPRINT(test, "TSIOBufferCopy", "TestCase1", TC_PASS, "ok");
2902     SDK_RPRINT(test, "TSIOBufferReaderCopy", "TestCase1", TC_PASS, "ok");
2903     test_passed = true;
2904   } else {
2905     SDK_RPRINT(test, "TSIOBufferWrite", "TestCase1", TC_FAIL, "failed");
2906     SDK_RPRINT(test, "TSIOBufferCopy", "TestCase1", TC_FAIL, "failed");
2907     SDK_RPRINT(test, "TSIOBufferReaderCopy", "TestCase1", TC_FAIL, "failed");
2908   }
2909 
2910   // Status of the whole test
2911   *pstatus = ((test_passed == true) ? REGRESSION_TEST_PASSED : REGRESSION_TEST_FAILED);
2912   return;
2913 }
2914 
2915 //////////////////////////////////////////////
2916 //       SDK_API_TSIOBuffer
2917 //
2918 // Unit Test for API: TSIOBuffer
2919 //                    TSIOBufferWrite
2920 //                    TSIOBufferReaderCopy
2921 //////////////////////////////////////////////
2922 
2923 REGRESSION_TEST(SDK_API_TSIOBufferBlockReadAvail)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2924 {
2925   bool test_passed_1 = false;
2926   bool test_passed_2 = false;
2927   *pstatus           = REGRESSION_TEST_INPROGRESS;
2928 
2929   int i           = 10000;
2930   TSIOBuffer bufp = TSIOBufferCreate();
2931   TSIOBufferWrite(bufp, (char *)&i, sizeof(int));
2932   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2933 
2934   int64_t avail_write, avail_read;
2935 
2936   // TODO: This is probably not correct any more.
2937   TSIOBufferBlock blockp = TSIOBufferStart(bufp);
2938 
2939   if ((TSIOBufferBlockWriteStart(blockp, &avail_write) - TSIOBufferBlockReadStart(blockp, readerp, &avail_read)) == sizeof(int)) {
2940     SDK_RPRINT(test, "TSIOBufferBlockReadStart", "TestCase1", TC_PASS, "ok");
2941     SDK_RPRINT(test, "TSIOBufferBlockWriteStart", "TestCase1", TC_PASS, "ok");
2942     test_passed_1 = true;
2943   } else {
2944     SDK_RPRINT(test, "TSIOBufferBlockReadStart", "TestCase1", TC_FAIL, "failed");
2945     SDK_RPRINT(test, "TSIOBufferBlockWriteStart", "TestCase1", TC_FAIL, "failed");
2946   }
2947 
2948   if ((TSIOBufferBlockReadAvail(blockp, readerp) + TSIOBufferBlockWriteAvail(blockp)) == 4096) {
2949     SDK_RPRINT(test, "TSIOBufferBlockReadAvail", "TestCase1", TC_PASS, "ok");
2950     SDK_RPRINT(test, "TSIOBufferBlockWriteAvail", "TestCase1", TC_PASS, "ok");
2951     test_passed_2 = true;
2952   } else {
2953     SDK_RPRINT(test, "TSIOBufferBlockReadAvail", "TestCase1", TC_FAIL, "failed");
2954     SDK_RPRINT(test, "TSIOBufferBlockWriteAvail", "TestCase1", TC_FAIL, "failed");
2955   }
2956 
2957   if (test_passed_1 && test_passed_2) {
2958     *pstatus = REGRESSION_TEST_PASSED;
2959   } else {
2960     *pstatus = REGRESSION_TEST_FAILED;
2961   }
2962 
2963   return;
2964 }
2965 
2966 //////////////////////////////////////////////////
2967 //       SDK_API_TSIOBuffer
2968 //
2969 // Unit Test for API: TSIOBufferBlockNext
2970 //////////////////////////////////////////////////
2971 
2972 REGRESSION_TEST(SDK_API_TSIOBufferBlockNext)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
2973 {
2974   bool test_passed = false;
2975   *pstatus         = REGRESSION_TEST_INPROGRESS;
2976 
2977   int i           = 10000;
2978   TSIOBuffer bufp = TSIOBufferCreate();
2979   TSIOBufferWrite(bufp, (char *)&i, sizeof(int));
2980 
2981   TSIOBufferReader readerp = TSIOBufferReaderAlloc(bufp);
2982   TSIOBufferBlock blockp   = TSIOBufferReaderStart(readerp);
2983 
2984   // TODO: This is probably not the best of regression tests right now ...
2985   // Note that this assumes block size is > sizeof(int) bytes.
2986   if (TSIOBufferBlockNext(blockp) == nullptr) {
2987     SDK_RPRINT(test, "TSIOBufferBlockNext", "TestCase1", TC_PASS, "ok");
2988     test_passed = true;
2989   } else {
2990     SDK_RPRINT(test, "TSIOBufferBlockNext", "TestCase1", TC_FAIL, "fail");
2991   }
2992 
2993   if (test_passed) {
2994     *pstatus = REGRESSION_TEST_PASSED;
2995   } else {
2996     *pstatus = REGRESSION_TEST_FAILED;
2997   }
2998 
2999   return;
3000 }
3001 
3002 REGRESSION_TEST(SDK_API_TSContSchedule)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
3003 {
3004   *pstatus = REGRESSION_TEST_INPROGRESS;
3005 
3006   // For asynchronous APIs, use static vars to store test and pstatus
3007   SDK_ContSchedule_test    = test;
3008   SDK_ContSchedule_pstatus = pstatus;
3009 
3010   TSCont contp  = TSContCreate(cont_schedule_handler, TSMutexCreate());
3011   TSCont contp2 = TSContCreate(cont_schedule_handler, TSMutexCreate());
3012 
3013   // Test Case 1: schedule immediate
3014   TSContScheduleOnPool(contp, 0, TS_THREAD_POOL_NET);
3015 
3016   // Test Case 2: schedule in 10ms
3017   TSContScheduleOnPool(contp2, 10, TS_THREAD_POOL_NET);
3018 }
3019 
3020 //////////////////////////////////////////////////////////////////////////////
3021 //     SDK_API_HttpHookAdd
3022 //
3023 // Unit Test for API: TSHttpHookAdd
3024 //                    TSHttpTxnReenable
3025 //                    TSHttpTxnClientIPGet
3026 //                    TSHttpTxnServerIPGet
3027 //                    TSHttpTxnIncomingAddrGet
3028 //                    TSHttpTxnClientAddrGet
3029 //                    TSHttpTxnClientReqGet
3030 //                    TSHttpTxnClientRespGet
3031 //                    TSHttpTxnServerReqGet
3032 //                    TSHttpTxnServerRespGet
3033 //                    TSHttpTxnNextHopAddrGet
3034 //                    TSHttpTxnClientProtocolStackGet
3035 //                    TSHttpTxnClientProtocolStackContains
3036 //////////////////////////////////////////////////////////////////////////////
3037 
3038 #define HTTP_HOOK_TEST_REQUEST_ID 1
3039 
3040 typedef struct {
3041   RegressionTest *regtest;
3042   int *pstatus;
3043   SocketServer *os;
3044   ClientTxn *browser;
3045   int hook_mask;
3046   int reenable_mask;
3047   bool test_client_ip_get;
3048   bool test_client_incoming_port_get;
3049   bool test_client_remote_port_get;
3050   bool test_client_req_get;
3051   bool test_client_resp_get;
3052   bool test_server_ip_get;
3053   bool test_server_req_get;
3054   bool test_server_resp_get;
3055   bool test_next_hop_ip_get;
3056   bool test_client_protocol_stack_get;
3057   bool test_client_protocol_stack_contains;
3058 
3059   unsigned int magic;
3060 } SocketTest;
3061 
3062 // This func is called by us from mytest_handler to test TSHttpTxnClientIPGet
3063 static int
3064 checkHttpTxnClientIPGet(SocketTest *test, void *data)
3065 {
3066   sockaddr const *ptr;
3067   in_addr_t ip;
3068   TSHttpTxn txnp      = (TSHttpTxn)data;
3069   in_addr_t actual_ip = htonl(INADDR_LOOPBACK); /* 127.0.0.1 is expected because the client is on the same machine */
3070 
3071   ptr = TSHttpTxnClientAddrGet(txnp);
3072   if (ptr == nullptr || INADDR_ANY == (ip = ats_ip4_addr_cast(ptr))) {
3073     test->test_client_ip_get = false;
3074     SDK_RPRINT(test->regtest, "TSHttpTxnClientIPGet", "TestCase1", TC_FAIL, "TSHttpTxnClientIPGet returns 0 %s",
3075                ptr ? "address" : "pointer");
3076     return TS_EVENT_CONTINUE;
3077   }
3078 
3079   if (ip == actual_ip) {
3080     test->test_client_ip_get = true;
3081     SDK_RPRINT(test->regtest, "TSHttpTxnClientIPGet", "TestCase1", TC_PASS, "ok [%0.8x]", ip);
3082   } else {
3083     test->test_client_ip_get = false;
3084     SDK_RPRINT(test->regtest, "TSHttpTxnClientIPGet", "TestCase1", TC_FAIL, "Value's Mismatch [expected %.8x got %.8x]", actual_ip,
3085                ip);
3086   }
3087   return TS_EVENT_CONTINUE;
3088 }
3089 
3090 // This func is called by us from mytest_handler to check for TSHttpTxnClientProtocolStackGet
3091 static int
3092 checkHttpTxnClientProtocolStackGet(SocketTest *test, void *data)
3093 {
3094   TSHttpTxn txnp = (TSHttpTxn)data;
3095   const char *results[10];
3096   int count = 0;
3097   TSHttpTxnClientProtocolStackGet(txnp, 10, results, &count);
3098   // Should return results[0] = "http/1.0", results[1] = "tcp", results[2] = "ipv4"
3099   test->test_client_protocol_stack_get = true;
3100   if (count != 3) {
3101     test->test_client_protocol_stack_get = false;
3102     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackGet", "TestCase1", TC_FAIL, "count should be 3 is %d", count);
3103   } else if (strcmp(results[0], "http/1.0") != 0) {
3104     test->test_client_protocol_stack_get = false;
3105     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackGet", "TestCase1", TC_FAIL, "results[0] should be http/1.0 is %s",
3106                results[0]);
3107   } else if (strcmp(results[1], "tcp") != 0) {
3108     test->test_client_protocol_stack_get = false;
3109     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackGet", "TestCase1", TC_FAIL, "results[1] should be tcp is %s",
3110                results[1]);
3111   } else if (strcmp(results[2], "ipv4") != 0) {
3112     test->test_client_protocol_stack_get = false;
3113     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackGet", "TestCase1", TC_FAIL, "results[2] should be ipv4 is %s",
3114                results[2]);
3115   } else {
3116     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackGet", "TestCase1", TC_PASS, "ok stack_size=%d", count);
3117   }
3118   return TS_EVENT_CONTINUE;
3119 }
3120 
3121 // This func is called by us from mytest_handler to check for TSHttpTxnClientProtocolStackContains
3122 static int
3123 checkHttpTxnClientProtocolStackContains(SocketTest *test, void *data)
3124 {
3125   TSHttpTxn txnp                            = (TSHttpTxn)data;
3126   const char *ret_tag                       = TSHttpTxnClientProtocolStackContains(txnp, "tcp");
3127   test->test_client_protocol_stack_contains = true;
3128   if (ret_tag) {
3129     const char *normalized_tag = TSNormalizedProtocolTag("tcp");
3130     if (normalized_tag != ret_tag) {
3131       SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackContains", "TestCase1", TC_FAIL,
3132                  "contains tcp, but normalized tag is wrong");
3133     } else {
3134       SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackContains", "TestCase1", TC_PASS, "ok tcp");
3135     }
3136   } else {
3137     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackContains", "TestCase1", TC_FAIL, "missing tcp");
3138     test->test_client_protocol_stack_contains = false;
3139   }
3140   ret_tag = TSHttpTxnClientProtocolStackContains(txnp, "udp");
3141   if (!ret_tag) {
3142     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackContains", "TestCase2", TC_PASS, "ok no udp");
3143   } else {
3144     SDK_RPRINT(test->regtest, "TSHttpTxnClientProtocolStackContains", "TestCase2", TC_FAIL, "faulty udp report");
3145     test->test_client_protocol_stack_contains = false;
3146   }
3147   return TS_EVENT_CONTINUE;
3148 }
3149 
3150 // This func is called by us from mytest_handler to check for TSHttpTxnNextHopIPGet
3151 static int
3152 checkHttpTxnNextHopIPGet(SocketTest *test, void *data)
3153 {
3154   TSHttpTxn txnp      = (TSHttpTxn)data;
3155   in_addr_t actual_ip = htonl(INADDR_LOOPBACK); /* 127.0.0.1 is expected because the client is on the same machine */
3156   sockaddr const *ptr;
3157   in_addr_t nexthopip;
3158 
3159   ptr = TSHttpTxnNextHopAddrGet(txnp);
3160   if (ptr == nullptr || (nexthopip = ats_ip4_addr_cast(ptr)) == 0) {
3161     test->test_next_hop_ip_get = false;
3162     SDK_RPRINT(test->regtest, "TSHttpTxnNextHopIPGet", "TestCase1", TC_FAIL, "TSHttpTxnNextHopIPGet returns 0 %s",
3163                ptr ? "address" : "pointer");
3164     return TS_EVENT_CONTINUE;
3165   }
3166 
3167   if (nexthopip == actual_ip) {
3168     test->test_next_hop_ip_get = true;
3169     SDK_RPRINT(test->regtest, "TSHttpTxnNextHopIPGet", "TestCase1", TC_PASS, "ok");
3170   } else {
3171     test->test_next_hop_ip_get = false;
3172     SDK_RPRINT(test->regtest, "TSHttpTxnNextHopIPGet", "TestCase1", TC_FAIL, "Value's Mismatch [expected %0.8x got %0.8x]",
3173                actual_ip, nexthopip);
3174   }
3175 
3176   return TS_EVENT_CONTINUE;
3177 }
3178 
3179 // This func is called by us from mytest_handler to test TSHttpTxnServerIPGet
3180 static int
3181 checkHttpTxnServerIPGet(SocketTest *test, void *data)
3182 {
3183   sockaddr const *ptr;
3184   in_addr_t ip;
3185   TSHttpTxn txnp      = (TSHttpTxn)data;
3186   in_addr_t actual_ip = htonl(INADDR_LOOPBACK); /* 127.0.0.1 is expected because the client is on the same machine */
3187 
3188   ptr = TSHttpTxnServerAddrGet(txnp);
3189   if (nullptr == ptr || 0 == (ip = ats_ip4_addr_cast(ptr))) {
3190     test->test_server_ip_get = false;
3191     SDK_RPRINT(test->regtest, "TSHttpTxnServerIPGet", "TestCase1", TC_FAIL, "TSHttpTxnServerIPGet returns 0 %s",
3192                ptr ? "address" : "pointer");
3193     return TS_EVENT_CONTINUE;
3194   }
3195 
3196   if (ip == actual_ip) {
3197     test->test_server_ip_get = true;
3198     SDK_RPRINT(test->regtest, "TSHttpTxnServerIPGet", "TestCase1", TC_PASS, "ok");
3199   } else {
3200     test->test_server_ip_get = false;
3201     SDK_RPRINT(test->regtest, "TSHttpTxnServerIPGet", "TestCase1", TC_FAIL, "Value's Mismatch");
3202   }
3203 
3204   return TS_EVENT_CONTINUE;
3205 }
3206 
3207 // This func is called by us from mytest_handler to test TSHttpTxnIncomingAddrGet
3208 static int
3209 checkHttpTxnIncomingAddrGet(SocketTest *test, void *data)
3210 {
3211   uint16_t port;
3212   const HttpProxyPort *proxy_port = HttpProxyPort::findHttp(AF_INET);
3213   TSHttpTxn txnp                  = (TSHttpTxn)data;
3214   sockaddr const *ptr             = TSHttpTxnIncomingAddrGet(txnp);
3215 
3216   if (nullptr == proxy_port) {
3217     SDK_RPRINT(test->regtest, "TSHttpTxnIncomingPortGet", "TestCase1", TC_FAIL,
3218                "TSHttpTxnIncomingAddrGet failed to find configured HTTP port.");
3219     test->test_client_incoming_port_get = false;
3220     return TS_EVENT_CONTINUE;
3221   }
3222   if (nullptr == ptr) {
3223     SDK_RPRINT(test->regtest, "TSHttpTxnIncomingPortGet", "TestCase1", TC_FAIL, "TSHttpTxnIncomingAddrGet returns 0 pointer");
3224     test->test_client_incoming_port_get = false;
3225     return TS_EVENT_CONTINUE;
3226   }
3227   port = ats_ip_port_host_order(ptr);
3228 
3229   TSDebug(UTDBG_TAG, "TS HTTP port = %x, Txn incoming client port %x", proxy_port->m_port, port);
3230 
3231   if (port == proxy_port->m_port) {
3232     SDK_RPRINT(test->regtest, "TSHttpTxnIncomingAddrGet", "TestCase1", TC_PASS, "ok");
3233     test->test_client_incoming_port_get = true;
3234   } else {
3235     SDK_RPRINT(test->regtest, "TSHttpTxnIncomingAddrGet", "TestCase1", TC_FAIL,
3236                "Value's Mismatch. From Function: %d  Expected value: %d", port, proxy_port->m_port);
3237     test->test_client_incoming_port_get = false;
3238   }
3239   return TS_EVENT_CONTINUE;
3240 }
3241 
3242 // This func is called by us from mytest_handler to test TSHttpTxnClientAddrGet
3243 static int
3244 checkHttpTxnClientAddrGet(SocketTest *test, void *data)
3245 {
3246   uint16_t port;
3247   uint16_t browser_port;
3248   TSHttpTxn txnp      = (TSHttpTxn)data;
3249   sockaddr const *ptr = TSHttpTxnClientAddrGet(txnp);
3250 
3251   browser_port = test->browser->local_port;
3252 
3253   if (nullptr == ptr) {
3254     SDK_RPRINT(test->regtest, "TSHttpTxnClientClientAddrGet", "TestCase2", TC_FAIL, "TSHttpTxnClientAddrGet returned 0 pointer.");
3255     test->test_client_remote_port_get = false;
3256     return TS_EVENT_CONTINUE;
3257   }
3258