1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #include "ats_fcgi_client.h"
20 #include "ats_fastcgi.h"
21 #include "fcgi_protocol.h"
22 #include "ts/ink_defs.h"
23 #include <iostream>
24 #include <sstream>
25 #include <string>
26 #include <locale>
27 #include <ts/ts.h>
28 #include <map>
29 #include <iterator>
30 #include "utils_internal.h"
31 #include <atscppapi/Headers.h>
32 #include <atscppapi/utils.h>
33 
34 using namespace atscppapi;
35 using namespace ats_plugin;
36 using namespace std;
37 
38 struct ats_plugin::FCGIClientState {
39   FCGI_BeginRequest *request;
40   FCGI_Header *header, *postHeader;
41   unsigned char *buff, *pBuffInc;
42   FCGIRecordList *records = nullptr;
43   TSHttpTxn txn_;
44   map<string, string> requestHeaders;
45   int request_id_;
46 
FCGIClientStateats_plugin::FCGIClientState47   FCGIClientState()
48     : request(nullptr), header(nullptr), postHeader(nullptr), buff(nullptr), pBuffInc(nullptr), records(nullptr), request_id_(0){};
49 
~FCGIClientStateats_plugin::FCGIClientState50   ~FCGIClientState()
51   {
52     request_id_ = 0;
53     free(request);
54     free(header);
55     TSfree(postHeader);
56     TSfree(buff);
57     TSfree(records);
58   };
59 };
60 // input to the constructor will be either unique transaction id or int type
61 // requestId
FCGIClientRequest(int request_id,TSHttpTxn txn)62 FCGIClientRequest::FCGIClientRequest(int request_id, TSHttpTxn txn)
63 {
64   first_chunk            = true;
65   state_                 = new FCGIClientState();
66   _headerRecord          = nullptr;
67   state_->txn_           = txn;
68   state_->request_id_    = request_id;
69   state_->requestHeaders = GenerateFcgiRequestHeaders();
70   // TODO Call printFCGIRequestHeaders() to printFCGIHeaders
71   // printFCGIRequestHeaders();
72   string str("POST"), value;
73   if (str.compare(state_->requestHeaders["REQUEST_METHOD"]) == 0) {
74     Transaction &transaction = utils::internal::getTransaction(state_->txn_);
75     Headers &h               = transaction.getClientRequest().getHeaders();
76 
77     if (h.isInitialized()) {
78       string key("Content-Length");
79       atscppapi::header_field_iterator it = h.find(key);
80       if (it != h.end()) {
81         atscppapi::HeaderField hf(*it);
82         string value                             = hf.values(","); // delimiter for header values
83         state_->requestHeaders["CONTENT_LENGTH"] = value.c_str();
84       }
85 
86       key = string("Content-type");
87       it  = h.find(key);
88       if (it != h.end()) {
89         HeaderField hf1(*it);
90         string value                           = hf1.values(","); // delimiter for header values
91         state_->requestHeaders["CONTENT_TYPE"] = value.c_str();
92       }
93     }
94 
95     int contentLength = 0;
96     string cl         = state_->requestHeaders["CONTENT_LENGTH"];
97     stringstream strToInt(cl);
98     strToInt >> contentLength;
99     state_->buff = (unsigned char *)TSmalloc(BUF_SIZE + contentLength);
100   } else {
101     state_->buff = (unsigned char *)TSmalloc(BUF_SIZE);
102   }
103 
104   state_->pBuffInc = state_->buff;
105 }
106 
107 // destructor will reset the client_req_id and delete the recListState_ object
108 // holding response records received from fcgi server
~FCGIClientRequest()109 FCGIClientRequest::~FCGIClientRequest()
110 {
111   if (_headerRecord)
112     delete _headerRecord;
113 
114   delete state_;
115 }
116 
117 bool
endsWith(const std::string & mainStr,const std::string & toMatch)118 endsWith(const std::string &mainStr, const std::string &toMatch)
119 {
120   if (mainStr.size() >= toMatch.size() && mainStr.compare(mainStr.size() - toMatch.size(), toMatch.size(), toMatch) == 0)
121     return true;
122   else
123     return false;
124 }
125 
126 map<string, string>
GenerateFcgiRequestHeaders()127 FCGIClientRequest::GenerateFcgiRequestHeaders()
128 {
129   map<string, string> fcgiReqHeader;
130   Transaction &transaction = utils::internal::getTransaction(state_->txn_);
131   Headers &h               = transaction.getClientRequest().getHeaders();
132   if (h.isInitialized()) {
133     for (auto it : h) {
134       atscppapi::HeaderField hf(it);
135       std::string str = hf.name().c_str();
136       std::string http("HTTP_");
137       std::locale loc;
138 
139       for (std::string::size_type i = 0; i < str.length(); ++i) {
140         http += std::toupper(str[i], loc);
141       }
142       fcgiReqHeader[http] = hf.values();
143     }
144   }
145 
146   // if string ends with '/' char then request global html file to server
147   string index;
148   string requestScript = transaction.getClientRequest().getUrl().getPath();
149   if (endsWith(requestScript, "/")) {
150     ats_plugin::FcgiPluginConfig *gConfig = InterceptGlobal::plugin_data->getGlobalConfigObj();
151     index                                 = gConfig->getHtml();
152     requestScript += index;
153   }
154 
155   fcgiReqHeader["DOCUMENT_ROOT"]     = InterceptGlobal::plugin_data->getGlobalConfigObj()->getDocumentRootDir();
156   fcgiReqHeader["SCRIPT_FILENAME"]   = fcgiReqHeader["DOCUMENT_ROOT"] + requestScript;
157   fcgiReqHeader["GATEWAY_INTERFACE"] = "FastCGI/1.1";
158   fcgiReqHeader["REQUEST_METHOD"]    = HTTP_METHOD_STRINGS[transaction.getClientRequest().getMethod()];
159   fcgiReqHeader["SCRIPT_NAME"]       = "/" + requestScript;
160   fcgiReqHeader["QUERY_STRING"]      = transaction.getClientRequest().getUrl().getQuery();
161   fcgiReqHeader["REQUEST_URI"]       = "/" + requestScript;
162 
163   // TODO map fcgiconfig with request headers.
164   // atsfcgiconfig::FCGIParams *params      = fcgiGlobal::plugin_data->getGlobalConfigObj()->getFcgiParams();
165   // atsfcgiconfig::FCGIParams::iterator it = params->begin();
166   // for (it = params->begin(); it != params->end(); ++it)
167   //   cout << it->first << " => " << it->second << endl;
168   fcgiReqHeader["SERVER_SOFTWARE"] = "ATS 7.1.1";
169   fcgiReqHeader["REMOTE_ADDR"]     = "127.0.0.1";
170   fcgiReqHeader["REMOTE_PORT"]     = "8090";
171   fcgiReqHeader["SERVER_ADDR"]     = "127.0.0.1";
172   fcgiReqHeader["SERVER_PORT"]     = "60000";
173   fcgiReqHeader["SERVER_NAME"]     = "ATS 7.1.1";
174   fcgiReqHeader["SERVER_PROTOCOL"] = "HTTP/1.1";
175   fcgiReqHeader["FCGI_ROLE"]       = "RESPONDER";
176   return fcgiReqHeader;
177 }
178 
179 void
printFCGIRequestHeaders()180 FCGIClientRequest::printFCGIRequestHeaders()
181 {
182   for (const auto &it : state_->requestHeaders) {
183     cout << it.first << " => " << it.second << endl;
184   }
185 }
186 
187 void
emptyParam()188 FCGIClientRequest::emptyParam()
189 {
190   string str("POST");
191   state_->pBuffInc = state_->buff;
192   // if Method is not post, then writing empty FCGI_STDIN to buffer
193   if (str.compare(state_->requestHeaders["REQUEST_METHOD"]) != 0) {
194     state_->postHeader                  = createHeader(FCGI_STDIN);
195     state_->postHeader->contentLengthB0 = 0;
196     state_->postHeader->contentLengthB1 = 0;
197     serialize(state_->pBuffInc, state_->postHeader, sizeof(FCGI_Header));
198     state_->pBuffInc += sizeof(FCGI_Header);
199     return;
200   }
201   TSDebug(PLUGIN_NAME, "empty Post Header Len: %ld ", state_->pBuffInc - state_->buff);
202 }
203 
204 FCGI_Header *
createHeader(uchar type)205 FCGIClientRequest::createHeader(uchar type)
206 {
207   FCGI_Header *tmp = (FCGI_Header *)calloc(1, sizeof(FCGI_Header));
208   tmp->version     = FCGI_VERSION_1;
209   tmp->type        = type;
210   fcgiHeaderSetRequestId(tmp, state_->request_id_);
211   return tmp;
212 }
213 
214 FCGI_BeginRequest *
createBeginRequest()215 FCGIClientRequest::createBeginRequest()
216 {
217   state_->request = (FCGI_BeginRequest *)TSmalloc(sizeof(FCGI_BeginRequest));
218   // TODO send the request id here
219   state_->request->header                  = createHeader(FCGI_BEGIN_REQUEST);
220   state_->request->body                    = (FCGI_BeginRequestBody *)calloc(1, sizeof(FCGI_BeginRequestBody));
221   state_->request->body->roleB0            = FCGI_RESPONDER;
222   state_->request->body->flags             = FCGI_KEEP_CONN;
223   state_->request->header->contentLengthB0 = sizeof(FCGI_BeginRequestBody);
224 
225   // serialize request header
226   serialize(state_->pBuffInc, state_->request->header, sizeof(FCGI_Header));
227   state_->pBuffInc += sizeof(FCGI_Header);
228   serialize(state_->pBuffInc, state_->request->body, sizeof(FCGI_BeginRequestBody));
229   state_->pBuffInc += sizeof(FCGI_BeginRequestBody);
230   TSDebug(PLUGIN_NAME, "Header Len: %ld ", state_->pBuffInc - state_->buff);
231   // FCGI Params headers
232   state_->header = createHeader(FCGI_PARAMS);
233   int len = 0, nb = 0;
234 
235   for (const auto &it : state_->requestHeaders) {
236     nb = serializeNameValue(state_->pBuffInc, it);
237     len += nb;
238   }
239 
240   state_->header->contentLengthB0 = BYTE_0(len);
241   state_->header->contentLengthB1 = BYTE_1(len);
242   TSDebug(PLUGIN_NAME, "ParamsLen: %d ContLenB0: %d ContLenB1: %d", len, state_->header->contentLengthB0,
243           state_->header->contentLengthB1);
244   serialize(state_->pBuffInc, state_->header, sizeof(FCGI_Header));
245   state_->pBuffInc += sizeof(FCGI_Header);
246 
247   for (const auto &it : state_->requestHeaders) {
248     nb = serializeNameValue(state_->pBuffInc, it);
249     state_->pBuffInc += nb;
250   }
251 
252   state_->header->contentLengthB0 = 0;
253   state_->header->contentLengthB1 = 0;
254   serialize(state_->pBuffInc, state_->header, sizeof(FCGI_Header));
255   state_->pBuffInc += sizeof(FCGI_Header);
256   return state_->request;
257 }
258 
259 void
postBodyChunk()260 FCGIClientRequest::postBodyChunk()
261 {
262   state_->pBuffInc   = state_->buff;
263   int dataLen        = 0;
264   state_->postHeader = createHeader(FCGI_STDIN);
265   dataLen            = postData.length();
266 
267   state_->postHeader->contentLengthB0 = BYTE_0(dataLen);
268   state_->postHeader->contentLengthB1 = BYTE_1(dataLen);
269   serialize(state_->pBuffInc, state_->postHeader, sizeof(FCGI_Header));
270   state_->pBuffInc += sizeof(FCGI_Header);
271   memcpy(state_->pBuffInc, postData.c_str(), dataLen);
272   state_->pBuffInc += dataLen;
273 
274   state_->postHeader->contentLengthB0 = 0;
275   state_->postHeader->contentLengthB1 = 0;
276   serialize(state_->pBuffInc, state_->postHeader, sizeof(FCGI_Header));
277   state_->pBuffInc += sizeof(FCGI_Header);
278   TSDebug(PLUGIN_NAME, "Serialized Post Data. Post Header Len: %ld ", state_->pBuffInc - state_->buff);
279 }
280 
281 unsigned char *
addClientRequest(int & dataLen)282 FCGIClientRequest::addClientRequest(int &dataLen)
283 {
284   dataLen = state_->pBuffInc - state_->buff;
285   return state_->buff;
286 }
287 
288 void
serialize(uchar * buffer,void * st,size_t size)289 FCGIClientRequest::serialize(uchar *buffer, void *st, size_t size)
290 {
291   memcpy(buffer, st, size);
292 }
293 
294 uint32_t
serializeNameValue(uchar * buffer,const std::pair<string,string> & it)295 FCGIClientRequest::serializeNameValue(uchar *buffer, const std::pair<string, string> &it)
296 {
297   uchar *p = buffer;
298   uint32_t nl, vl;
299   nl = it.first.length();
300   vl = it.second.length();
301 
302   if (nl < 128) {
303     *p++ = BYTE_0(nl);
304   } else {
305     *p++ = BYTE_3(nl);
306     *p++ = BYTE_2(nl);
307     *p++ = BYTE_1(nl);
308     *p++ = BYTE_0(nl);
309   }
310 
311   if (vl < 128) {
312     *p++ = BYTE_0(vl);
313   } else {
314     *p++ = BYTE_3(vl);
315     *p++ = BYTE_2(vl);
316     *p++ = BYTE_1(vl);
317     *p++ = BYTE_0(vl);
318   }
319   memcpy(p, it.first.c_str(), nl);
320   p += nl;
321   memcpy(p, it.second.c_str(), vl);
322   p += vl;
323   return p - buffer;
324 }
325 
326 void
fcgiHeaderSetRequestId(FCGI_Header * h,int request_id)327 FCGIClientRequest::fcgiHeaderSetRequestId(FCGI_Header *h, int request_id)
328 {
329   h->requestIdB0 = BYTE_0(request_id);
330   h->requestIdB1 = BYTE_1(request_id);
331 }
332 
333 void
fcgiHeaderSetContentLen(FCGI_Header * h,uint16_t len)334 FCGIClientRequest::fcgiHeaderSetContentLen(FCGI_Header *h, uint16_t len)
335 {
336   h->contentLengthB0 = BYTE_0(len);
337   h->contentLengthB1 = BYTE_1(len);
338 }
339 
340 uint32_t
fcgiHeaderGetContentLen(FCGI_Header * h)341 FCGIClientRequest::fcgiHeaderGetContentLen(FCGI_Header *h)
342 {
343   return (h->contentLengthB1 << 8) + h->contentLengthB0;
344 }
345 
346 int
fcgiProcessHeader(uchar ch,FCGIRecordList * rec)347 FCGIClientRequest::fcgiProcessHeader(uchar ch, FCGIRecordList *rec)
348 {
349   FCGI_Header *h;
350   FCGI_State *state = &(rec->state);
351   h                 = rec->header;
352 
353   switch (*state) {
354   case fcgi_state_version:
355     h->version = ch;
356     *state     = fcgi_state_type;
357     break;
358   case fcgi_state_type:
359     h->type = ch;
360     *state  = fcgi_state_request_id_hi;
361     break;
362   case fcgi_state_request_id_hi:
363     h->requestIdB1 = ch;
364     *state         = fcgi_state_request_id_lo;
365     break;
366   case fcgi_state_request_id_lo:
367     h->requestIdB0 = ch;
368     *state         = fcgi_state_content_len_hi;
369     break;
370   case fcgi_state_content_len_hi:
371     h->contentLengthB1 = ch;
372     *state             = fcgi_state_content_len_lo;
373     break;
374   case fcgi_state_content_len_lo:
375     h->contentLengthB0 = ch;
376     *state             = fcgi_state_padding_len;
377     break;
378   case fcgi_state_padding_len:
379     h->paddingLength = ch;
380     *state           = fcgi_state_reserved;
381     break;
382   case fcgi_state_reserved:
383     h->reserved = ch;
384     *state      = fcgi_state_content_begin;
385     break;
386 
387   case fcgi_state_content_begin:
388   case fcgi_state_content_proc:
389   case fcgi_state_padding:
390   case fcgi_state_done:
391     return FCGI_PROCESS_DONE;
392   }
393   return FCGI_PROCESS_AGAIN;
394 }
395 
396 int
fcgiProcessContent(uchar ** beg_buf,uchar * end_buf,FCGIRecordList * rec)397 FCGIClientRequest::fcgiProcessContent(uchar **beg_buf, uchar *end_buf, FCGIRecordList *rec)
398 {
399   size_t tot_len, con_len, cpy_len, offset, nb = end_buf - *beg_buf;
400   FCGI_State *state = &(rec->state);
401   FCGI_Header *h    = rec->header;
402   offset            = rec->offset;
403 
404   if (*state == fcgi_state_padding) {
405     *state = fcgi_state_done;
406     *beg_buf += (size_t)((int)rec->length - (int)offset + (int)h->paddingLength);
407     return FCGI_PROCESS_DONE;
408   }
409 
410   con_len = rec->length - offset;
411   tot_len = con_len + h->paddingLength;
412 
413   if (con_len <= nb)
414     cpy_len = con_len;
415   else {
416     cpy_len = nb;
417   }
418 
419   memcpy(rec->content + offset, *beg_buf, cpy_len);
420 
421   if (tot_len <= nb) {
422     rec->offset += tot_len;
423     *state = fcgi_state_done;
424     *beg_buf += tot_len;
425     return FCGI_PROCESS_DONE;
426   } else if (con_len <= nb) {
427     /* Have to still skip all or some of padding */
428     *state = fcgi_state_padding;
429     rec->offset += nb;
430     *beg_buf += nb;
431     return FCGI_PROCESS_AGAIN;
432   } else {
433     rec->offset += nb;
434     *beg_buf += nb;
435     return FCGI_PROCESS_AGAIN;
436   }
437 
438   return 0;
439 }
440 
441 int
fcgiProcessRecord(uchar ** beg_buf,uchar * end_buf,FCGIRecordList * rec)442 FCGIClientRequest::fcgiProcessRecord(uchar **beg_buf, uchar *end_buf, FCGIRecordList *rec)
443 {
444   int rv;
445   while (rec->state < fcgi_state_content_begin) {
446     if ((rv = fcgiProcessHeader(**beg_buf, rec)) == FCGI_PROCESS_ERR)
447       return FCGI_PROCESS_ERR;
448     (*beg_buf)++;
449     if (*beg_buf == end_buf)
450       return FCGI_PROCESS_AGAIN;
451   }
452   if (rec->state == fcgi_state_content_begin) {
453     rec->length  = fcgiHeaderGetContentLen(rec->header);
454     rec->content = (uchar *)TSmalloc(rec->length);
455     rec->state   = (FCGI_State)(int(rec->state) + 1);
456   }
457 
458   return fcgiProcessContent(beg_buf, end_buf, rec);
459 }
460 
461 static char *
convert_mime_hdr_to_string(TSMBuffer bufp,TSMLoc hdr_loc)462 convert_mime_hdr_to_string(TSMBuffer bufp, TSMLoc hdr_loc)
463 {
464   TSIOBuffer output_buffer;
465   TSIOBufferReader reader;
466   int64_t total_avail;
467 
468   TSIOBufferBlock block;
469   const char *block_start;
470   int64_t block_avail;
471 
472   char *output_string;
473   int output_len;
474 
475   output_buffer = TSIOBufferCreate();
476 
477   if (!output_buffer) {
478     TSError("[InkAPITest] couldn't allocate IOBuffer");
479   }
480 
481   reader = TSIOBufferReaderAlloc(output_buffer);
482 
483   /* This will print  just MIMEFields and not
484      the http request line */
485   TSMimeHdrPrint(bufp, hdr_loc, output_buffer);
486 
487   /* Find out how the big the complete header is by
488      seeing the total bytes in the buffer.  We need to
489      look at the buffer rather than the first block to
490      see the size of the entire header */
491   total_avail = TSIOBufferReaderAvail(reader);
492 
493   /* Allocate the string with an extra byte for the string
494      terminator */
495   output_string = (char *)TSmalloc(total_avail + 1);
496   output_len    = 0;
497 
498   /* We need to loop over all the buffer blocks to make
499      sure we get the complete header since the header can
500      be in multiple blocks */
501   block = TSIOBufferReaderStart(reader);
502   while (block) {
503     block_start = TSIOBufferBlockReadStart(block, reader, &block_avail);
504 
505     /* We'll get a block pointer back even if there is no data
506        left to read so check for this condition and break out of
507        the loop. A block with no data to read means we've exhausted
508        buffer of data since if there was more data on a later
509        block in the chain, this block would have been skipped over */
510     if (block_avail == 0) {
511       break;
512     }
513 
514     memcpy(output_string + output_len, block_start, block_avail);
515     output_len += block_avail;
516 
517     /* Consume the data so that we get to the next block */
518     TSIOBufferReaderConsume(reader, block_avail);
519 
520     /* Get the next block now that we've consumed the
521        data off the last block */
522     block = TSIOBufferReaderStart(reader);
523   }
524 
525   /* Terminate the string */
526   output_string[output_len] = '\0';
527   output_len++;
528 
529   /* Free up the TSIOBuffer that we used to print out the header */
530   TSIOBufferReaderFree(reader);
531   TSIOBufferDestroy(output_buffer);
532 
533   return output_string;
534 }
535 
536 bool
fcgiProcessBuffer(uchar * beg_buf,uchar * end_buf,std::ostringstream & output)537 FCGIClientRequest::fcgiProcessBuffer(uchar *beg_buf, uchar *end_buf, std::ostringstream &output)
538 {
539   if (!_headerRecord)
540     _headerRecord = new FCGIRecordList;
541 
542   while (1) {
543     if (_headerRecord->state == fcgi_state_done) {
544       FCGIRecordList *tmp = _headerRecord;
545       _headerRecord       = new FCGIRecordList();
546       delete tmp;
547     }
548 
549     if (fcgiProcessRecord(&beg_buf, end_buf, _headerRecord) == FCGI_PROCESS_DONE) {
550       if (first_chunk) {
551         string start = std::string((char *)_headerRecord->content, _headerRecord->length);
552         string end("\r\n\r\n");
553         string headerString;
554         int foundPos = start.find(end);
555         if (foundPos != -1) {
556           headerString = start.substr(0, foundPos + 4);
557         }
558         const char *buff = headerString.c_str();
559         const char *start1;
560         const char *endPtr;
561         start1 = buff;
562         endPtr = buff + strlen(buff) + 1;
563         char *temp;
564         TSMLoc mime_hdr_loc1 = (TSMLoc) nullptr;
565         TSParseResult retval;
566         TSMimeParser parser = TSMimeParserCreate();
567         TSMBuffer bufp      = TSMBufferCreate();
568         TSMimeHdrCreate(bufp, &mime_hdr_loc1);
569         if ((retval = TSMimeHdrParse(parser, bufp, mime_hdr_loc1, &start1, endPtr)) == TS_PARSE_ERROR) {
570           TSDebug(PLUGIN_NAME, "[FCGIClientRequest:%s] Hdr Parse Error.", __FUNCTION__);
571         } else {
572           if (retval == TS_PARSE_DONE) {
573             temp = convert_mime_hdr_to_string(bufp, mime_hdr_loc1); // Implements TSMimeHdrPrint.
574             if (strcmp(buff, temp) == 0) {
575               Headers h(bufp, mime_hdr_loc1);
576               string key("Status");
577               if (h.isInitialized()) {
578                 atscppapi::header_field_iterator it = h.find(key);
579                 if (it != h.end()) {
580                   atscppapi::HeaderField hf(*it);
581                   string value = hf.values(","); // delimiter for header values
582                   output << HTTP_VERSION_STRINGS[HTTP_VERSION_1_1] << " ";
583                   output << value.c_str() << "\r\n";
584                 } else {
585                   output << "HTTP/1.0 200 OK\r\n";
586                 }
587                 // it = h.begin();
588                 // for (it = h.begin(); it != h.end(); ++it) {
589                 //   atscppapi::HeaderField hf(*it);
590                 //   std::cout << "Name => " << hf.name() << "Value => " << hf.values() << std::endl;
591                 // }
592               }
593             } else {
594               TSDebug(PLUGIN_NAME, "[FCGIClientRequest:%s] Incorrect Parsing.", __FUNCTION__);
595               output << "HTTP/1.0 200 OK\r\n";
596             }
597             TSfree(temp);
598           }
599         }
600 
601         TSMimeHdrDestroy(bufp, mime_hdr_loc1);
602         TSHandleMLocRelease(bufp, TS_NULL_MLOC, mime_hdr_loc1);
603         TSMBufferDestroy(bufp);
604         TSMimeParserDestroy(parser);
605         // OS(XXX): merge -- check later, currently leaning towards that we do not have
606         // to do this.
607         // TSfree(buff);
608         first_chunk = false;
609       }
610       if (_headerRecord->header->type == FCGI_STDOUT) {
611         output << std::string((const char *)_headerRecord->content, _headerRecord->length);
612       }
613       if (_headerRecord->header->type == FCGI_STDERR) {
614         // XXX(oschaaf): we may want to treat this differently, but for now this will do.
615         output << "HTTP/1.0 500 Server Error\r\n\r\n";
616         output << std::string((const char *)_headerRecord->content, _headerRecord->length);
617         TSDebug(PLUGIN_NAME, "[ FCGIClientRequest:%s ] Response FCGI_STDERR.*****\n\n", __FUNCTION__);
618         return true;
619       }
620       if (_headerRecord->header->type == FCGI_END_REQUEST) {
621         TSDebug(PLUGIN_NAME, "[ FCGIClientRequest:%s ] Response FCGI_END_REQUEST.*****\n\n", __FUNCTION__);
622         return true;
623       }
624     }
625 
626     if (beg_buf == end_buf)
627       return false;
628   }
629 }
630 
631 bool
fcgiDecodeRecordChunk(uchar * beg_buf,size_t remain,std::ostringstream & output)632 FCGIClientRequest::fcgiDecodeRecordChunk(uchar *beg_buf, size_t remain, std::ostringstream &output)
633 {
634   return fcgiProcessBuffer((uchar *)beg_buf, (uchar *)beg_buf + (size_t)remain, output);
635 }
636 
637 void
print_bytes(uchar * buf,int n)638 FCGIClientRequest::print_bytes(uchar *buf, int n)
639 {
640   int i;
641   printf("{");
642   for (i = 0; i < n; i++)
643     printf("%02x", buf[i]);
644   printf("}\n");
645 }
646