Drizzled Public API Documentation

CSHTTPStream.cc

00001 /* Copyright (C) 2008 PrimeBase Technologies GmbH, Germany
00002  *
00003  * PrimeBase Media Stream for MySQL
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
00018  *
00019  * Original author: Paul McCullagh (H&G2JCtL)
00020  * Continued development: Barry Leslie
00021  *
00022  * 2007-06-10
00023  *
00024  * A basic syncronized object.
00025  *
00026  */
00027 #include "CSConfig.h"
00028 
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #include <inttypes.h>
00032 
00033 #include "CSGlobal.h"
00034 #include "CSHTTPStream.h"
00035 #include "CSLog.h"
00036 
00037 #ifdef DEBUG
00038 //#define PRINT_HEADER
00039 #endif
00040 
00041 /*
00042  * ---------------------------------------------------------------
00043  * HTTP HEADERS
00044  */
00045 
00046 CSHeader::~CSHeader()
00047 {
00048   if (iName) {
00049     iName->release();
00050     iName = NULL;
00051   }
00052   if (iValue) {
00053     iValue->release();
00054     iValue = NULL;
00055   }
00056 }
00057 
00058 void CSHeader::setName(const char *name)
00059 {
00060   iName = CSString::newString(name);
00061 }
00062 
00063 void CSHeader::setName(const char *name, uint32_t len)
00064 {
00065   iName = CSString::newString(name, len);
00066 }
00067 
00068 void CSHeader::setName(CSString *name)
00069 {
00070   iName = name;
00071 }
00072 
00073 void CSHeader::setValue(const char *value)
00074 {
00075   iValue = CSString::newString(value);
00076 }
00077 
00078 void CSHeader::setValue(const char *value, uint32_t len)
00079 {
00080   iValue = CSString::newString(value, len);
00081 }
00082 
00083 void CSHeader::setValue(CSString *value)
00084 {
00085   iValue = value;
00086 }
00087 
00088 void CSHeader::write(CSOutputStream *out, bool trace)
00089 {
00090   if (trace)
00091     printf("%s: %s\n", iName->getCString(), iValue->getCString());
00092 
00093   out->print(iName);
00094   out->print(": ");
00095   if (iValue)
00096     out->print(iValue);
00097   out->print("\r\n");
00098 }
00099 
00100 void CSHTTPHeaders::clearHeaders()
00101 {
00102   iKeepAlive = false;
00103   iExpect100Continue = false;
00104   iUnknownEpectHeader = false;
00105   if (iHeaders) {
00106     iHeaders->release();
00107     iHeaders = NULL;
00108   }
00109 }
00110 CSVector *CSHTTPHeaders::takeHeaders()
00111 {
00112   CSVector *headers = iHeaders;
00113   iHeaders = NULL;
00114   return headers;
00115 }
00116 
00117 void CSHTTPHeaders::setHeaders(CSVector *headers)
00118 {
00119   if (iHeaders) 
00120     iHeaders->release();
00121   iHeaders = headers;
00122 }
00123 
00124 void CSHTTPHeaders::addHeader(CSHeader *h)
00125 {
00126   if (!iHeaders)
00127     new_(iHeaders, CSVector(5));
00128 
00129   if (strcasecmp(h->getNameCString(), "Connection") == 0 && strcasecmp(h->getValueCString(), "Keep-Alive") == 0)
00130     iKeepAlive = true;
00131     
00132   if (strcasecmp(h->getNameCString(), "Expect") == 0) {
00133     if (strcasecmp(h->getValueCString(), "100-continue") == 0)
00134       iExpect100Continue = true;
00135     else
00136       iUnknownEpectHeader = true;
00137   }
00138     
00139   iHeaders->add(h);
00140 }
00141 
00142 void CSHTTPHeaders::addHeaders(CSHTTPHeaders *headers)
00143 {
00144   CSHeader *h;
00145   uint32_t i =0;
00146   while ((h = headers->getHeader(i++))) {
00147     addHeader(h);
00148   }
00149 }
00150 
00151 void CSHTTPHeaders::addHeader(const char *name, const char *value)
00152 {
00153   CSHeader *h;
00154 
00155   enter_();
00156   if (!iHeaders)
00157     new_(iHeaders, CSVector(5));
00158 
00159   new_(h, CSHeader());
00160   push_(h);
00161   h->setName(name);
00162   h->setValue(value);
00163   pop_(h);
00164 
00165   addHeader(h);
00166   exit_();
00167 }
00168 
00169 void CSHTTPHeaders::addHeader(const char *name, uint32_t nlen, const char *value, uint32_t vlen)
00170 {
00171   CSHeader *h;
00172 
00173   enter_();
00174   if (!iHeaders)
00175     new_(iHeaders, CSVector(5));
00176 
00177   new_(h, CSHeader());
00178   push_(h);
00179   h->setName(name, nlen);
00180   h->setValue(value, vlen);
00181   pop_(h);
00182   addHeader(h);
00183   exit_();
00184 }
00185 
00186 void CSHTTPHeaders::addHeader(CSString *name, CSString *value)
00187 {
00188   CSHeader *h;
00189 
00190   enter_();
00191   push_(name);
00192   push_(value);
00193   if (!iHeaders)
00194     new_(iHeaders, CSVector(5));
00195 
00196   new_(h, CSHeader());
00197   pop_(value);
00198   pop_(name);
00199   h->setName(name);
00200   h->setValue(value);
00201   addHeader(h);
00202   exit_();
00203 }
00204 
00205 void CSHTTPHeaders::addHeader(const char *name, CSString *value)
00206 {
00207   CSHeader *h;
00208   CSString *n;
00209 
00210   enter_();
00211   push_(value);
00212   n = CSString::newString(name);
00213   push_(n);
00214   if (!iHeaders)
00215     new_(iHeaders, CSVector(5));
00216   new_(h, CSHeader());
00217   pop_(n);
00218   pop_(value);
00219   h->setName(n);
00220   h->setValue(value);
00221   addHeader(h);
00222   exit_();
00223 }
00224 
00225 void CSHTTPHeaders::addHeader(const char *name, uint64_t value)
00226 {
00227   char buffer[30];
00228 
00229   snprintf(buffer, 30, "%"PRIu64, value);
00230   addHeader(name, buffer);
00231 }
00232 
00233 void CSHTTPHeaders::removeHeader(CSString *name)
00234 {
00235   enter_();
00236   push_(name);
00237   if (iHeaders) {
00238     CSHeader *h;
00239 
00240     for (uint32_t i=0; i<iHeaders->size(); ) {
00241       h = (CSHeader *) iHeaders->get(i);
00242       if (h->getName()->compare(name) == 0) {
00243         iHeaders->remove(i);
00244       } else 
00245         i++;
00246     }
00247   }
00248   release_(name);
00249   
00250   exit_();
00251 }
00252 
00253 void CSHTTPHeaders::removeHeader(const char *name)
00254 {
00255   removeHeader(CSString::newString(name));
00256 }
00257 
00258 CSString *CSHTTPHeaders::getHeaderValue(const char *name)
00259 {
00260   CSString *v;
00261 
00262   v = NULL;
00263   if (iHeaders) {
00264     CSHeader *h;
00265 
00266     for (uint32_t i=0; i<iHeaders->size(); i++) {
00267       h = (CSHeader *) iHeaders->get(i);
00268       if (h->getName()->compare(name) == 0) {
00269         v = h->getValue();
00270         v->retain();
00271         break;
00272       }
00273     }
00274   }
00275   return v;
00276 }
00277 
00278 const char *CSHTTPHeaders::getHeaderCStringValue(const char *name)
00279 {
00280   if (iHeaders) {
00281     CSHeader *h;
00282 
00283     for (uint32_t i=0; i<iHeaders->size(); i++) {
00284       h = (CSHeader *) iHeaders->get(i);
00285       if (h->getName()->compare(name) == 0) {
00286         return h->getValue()->getCString();
00287       }
00288     }
00289   }
00290   return NULL;
00291 }
00292 
00293 void CSHTTPHeaders::writeHeader(CSOutputStream *out, bool trace)
00294 {
00295   if (iHeaders) {
00296     CSHeader *h;
00297 
00298     for (uint32_t i=0; i<iHeaders->size(); i++) {
00299       h = (CSHeader *) iHeaders->get(i);
00300       h->write(out, trace);
00301     }
00302   }
00303 }
00304 
00305 bool CSHTTPHeaders::keepAlive()
00306 {
00307   return iKeepAlive;
00308 }
00309 
00310 bool CSHTTPHeaders::expect100Continue()
00311 {
00312   return iExpect100Continue;
00313 }
00314 
00315 bool CSHTTPHeaders::unknownEpectHeader()
00316 {
00317   return iUnknownEpectHeader;
00318 }
00319 
00320 /*
00321  * ---------------------------------------------------------------
00322  * HTTP INPUT STREAMS
00323  */
00324 
00325 CSHTTPInputStream::CSHTTPInputStream(CSInputStream* in):
00326 CSHTTPHeaders(),
00327 iInput(NULL),
00328 iMethod(NULL),
00329 iRequestURI(NULL),
00330 iHTTPVersion(NULL),
00331 iStatusPhrase(NULL)
00332 {
00333   iInput = in;
00334 }
00335 
00336 CSHTTPInputStream::~CSHTTPInputStream()
00337 {
00338   freeHead();
00339   if (iInput)
00340     iInput->release();
00341 }
00342 
00343 void CSHTTPInputStream::readHead(bool trace)
00344 {
00345   CSStringBuffer  *sb = NULL;
00346   bool      first_line = true;
00347   uint32_t      start, end;
00348 
00349   enter_();
00350   freeHead();
00351   for (;;) {
00352     sb = iInput->readLine();
00353     if (!sb)
00354       break;
00355     if (trace) {
00356       if (first_line)
00357         CSL.log(self, CSLog::Protocol, "HTTP Request - Header:\n");
00358       printf("%s\n", sb->getCString());
00359     }
00360     if (sb->length() == 0) {
00361       sb->release();
00362       break;
00363     }
00364     push_(sb);
00365     
00366     if (first_line) {
00367       CSString *str;
00368       start = sb->ignore(0, ' ');
00369       end = sb->find(start, ' ');
00370       str = sb->substr(start, end - start);
00371       if (str->startsWith("HTTP")) { // Reply header
00372         iMethod = NULL;
00373         iRequestURI = NULL;
00374         iHTTPVersion = str;
00375         start = sb->ignore(end, ' ');
00376         end = sb->find(start, ' ');
00377         if (start > end)
00378           CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
00379 
00380         str = sb->substr(start, end - start);
00381         iStatus = atol(str->getCString());
00382         str->release();
00383         start = sb->ignore(end, ' ');
00384         end = sb->find(start, '\r');
00385         if (start > end)
00386           CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
00387         iStatusPhrase = sb->substr(start, end - start);
00388       } else {
00389         iStatus = 0;
00390         iStatusPhrase = NULL;
00391         iMethod = str;
00392       start = sb->ignore(end, ' ');
00393       end = sb->find(start, ' ');
00394       if (start > end)
00395         CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
00396       iRequestURI = sb->substr(start, end - start);
00397       start = sb->ignore(end, ' ');
00398       end = sb->find(start, ' ');
00399       if (start > end)
00400         CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
00401       iHTTPVersion = sb->substr(start, end - start);
00402       }         
00403       first_line = false;
00404     }
00405     else {
00406       uint32_t nstart, nend;
00407       uint32_t vstart, vend;
00408 
00409       nstart = sb->ignore(0, ' ');
00410       nend = sb->find(nstart, ':');
00411 
00412       vstart = sb->ignore(nend+1, ' ');
00413       vend = sb->find(vstart, '\r');
00414 
00415       nend = sb->trim(nend, ' ');
00416       vend = sb->trim(vend, ' ');
00417       
00418       if (vstart > vend)
00419         CSException::throwException(CS_CONTEXT, CS_ERR_BAD_HTTP_HEADER, "Bad HTTP header");
00420       addHeader(sb->getBuffer(nstart), nend-nstart, sb->getBuffer(vstart), vend-vstart);
00421     }
00422 
00423     release_(sb);
00424   }
00425   exit_();
00426 }
00427 
00428 void CSHTTPInputStream::readBody()
00429 {
00430   uint64_t  body_size;
00431   size_t  tfer, len;
00432 
00433   if (getContentLength(&body_size)) {
00434     iBody.setLength((size_t) body_size);
00435     len = 0;
00436     while (len < body_size) {
00437       tfer =  read(iBody.getBuffer(len), (size_t)(body_size - len));
00438       if (!tfer)
00439         CSException::throwException(CS_CONTEXT, CS_ERR_BODY_INCOMPLETE, "POST data incomplete");
00440       len += tfer;
00441     }
00442   }
00443   else {
00444     CSStringBuffer *sb = NULL;
00445 
00446     /* Read until we have an empty line. */
00447     for (;;) {
00448       sb = readLine();
00449       if (!sb)
00450         break;
00451       if (sb->length() == 0) {
00452         sb->release();
00453         break;
00454       }
00455       iBody.append(sb->getBuffer(0), sb->length());
00456       iBody.append((char) '\n');
00457       sb->release();
00458     }
00459   }
00460 }
00461 
00462 bool CSHTTPInputStream::getRange(uint64_t *size, uint64_t *offset)
00463 {
00464   CSString  *val;
00465   bool haveRange = false;
00466 
00467   if ((val = getHeaderValue("Range"))) {
00468     uint64_t    first_byte = 0, last_byte = 0;
00469     const char  *range = val->getCString();
00470     
00471     if (range && (val->compare("bytes=", 6) == 0)) {
00472       if ((sscanf(range + 6, "%"PRIu64"-%"PRIu64"", &first_byte, &last_byte) == 2) && (last_byte >= first_byte)) {
00473         *offset = (uint64_t) first_byte;
00474         *size =last_byte - first_byte + 1;
00475         haveRange = true;
00476       } 
00477     }
00478     val->release();
00479         
00480   }
00481   return haveRange;
00482 }
00483 
00484 bool CSHTTPInputStream::getContentLength(uint64_t *length)
00485 {
00486   CSString  *val;
00487   uint64_t    size = 0;
00488 
00489   if ((val = getHeaderValue("Content-Length"))) {
00490     const char  *len = val->getCString();
00491 
00492     if (len)  
00493       sscanf(len, "%"PRIu64"", &size);    
00494     val->release();
00495     *length = size;
00496     return true;
00497   }
00498   return false;
00499 }
00500 
00501 const char *CSHTTPInputStream::getMethod()
00502 {
00503   if (!iMethod)
00504     return NULL;
00505   return iMethod->getCString();
00506 }
00507 
00508 void CSHTTPInputStream::close()
00509 {
00510   enter_();
00511   iInput->close();
00512   exit_();
00513 }
00514 
00515 size_t CSHTTPInputStream::read(char *b, size_t len)
00516 {
00517   enter_();
00518   return_(iInput->read(b, len));
00519 }
00520 
00521 int CSHTTPInputStream::read()
00522 {
00523   enter_();
00524   return_(iInput->read());
00525 }
00526 
00527 int CSHTTPInputStream::peek()
00528 {
00529   enter_();
00530   return_(iInput->peek());
00531 }
00532 
00533 void CSHTTPInputStream::freeHead()
00534 {
00535   enter_();
00536   clearHeaders();
00537   if (iMethod) {
00538     iMethod->release();
00539     iMethod = NULL;
00540   }
00541   if (iRequestURI) {
00542     iRequestURI->release();
00543     iRequestURI = NULL;
00544   }
00545   if (iHTTPVersion) {
00546     iHTTPVersion->release();
00547     iHTTPVersion = NULL;
00548   }
00549   if (iStatusPhrase) {
00550     iStatusPhrase->release();
00551     iStatusPhrase = NULL;
00552   }
00553   iStatus = 0;
00554   exit_();
00555 }
00556 
00557 CSHTTPInputStream *CSHTTPInputStream::newStream(CSInputStream* i)
00558 {
00559   CSHTTPInputStream *s;
00560 
00561   if (!(s = new CSHTTPInputStream(i))) {
00562     i->release();
00563     CSException::throwOSError(CS_CONTEXT, ENOMEM);
00564   }
00565   return s;
00566 }
00567 
00568 /*
00569  * ---------------------------------------------------------------
00570  * HTTP OUTPUT STREAMS
00571  */
00572 
00573 CSHTTPOutputStream::CSHTTPOutputStream(CSOutputStream* out):
00574 CSHTTPHeaders(),
00575 iOutput(NULL),
00576 iStatus(0),
00577 iContentLength(0),
00578 iRangeSize(0),
00579 iRangeOffset(0),
00580 iTotalLength(0)
00581 {
00582   iOutput = out;
00583   iBody.setGrowSize(120);
00584 }
00585 
00586 CSHTTPOutputStream::~CSHTTPOutputStream()
00587 {
00588   clearHeaders();
00589   clearBody();
00590   if (iOutput)
00591     iOutput->release();
00592 }
00593 
00594 void CSHTTPOutputStream::print(const char *str, bool trace)
00595 {
00596   if (trace)
00597     printf("%s", str);
00598   iOutput->print(str);
00599 }
00600 
00601 
00602 void CSHTTPOutputStream::print(int32_t value, bool trace)
00603 {
00604   if (trace)
00605     printf("%d", value);
00606   iOutput->print(value);
00607 }
00608 
00609 void CSHTTPOutputStream::print(uint64_t value, bool trace)
00610 {
00611   if (trace)
00612     printf("%"PRIu64, value);
00613   iOutput->print(value);
00614 }
00615 
00616 void CSHTTPOutputStream::writeHeaders(bool trace)
00617 {
00618   writeHeader(this, trace);
00619   clearHeaders();
00620 }
00621 
00622 void CSHTTPOutputStream::writeHead(bool trace)
00623 {
00624   enter_();
00625   if (trace)
00626     CSL.log(self, CSLog::Protocol, "HTTP Reply - Header:\n");
00627   print("HTTP/1.1 ", trace);
00628   print(iStatus, trace);
00629   print(" ", trace);
00630   print(getReasonPhrase(iStatus), trace);
00631   print("\r\n", trace);
00632   writeHeader(iOutput, trace);
00633   print("Content-Length: ", trace);
00634   print(iContentLength, trace);
00635   print("\r\n", trace);
00636   if (iRangeSize && (iStatus == 200)) {
00637     print("Content-Range: bytes ", trace);
00638     print(iRangeOffset, trace);
00639     print("-", trace);
00640     print(iRangeOffset + iRangeSize -1, trace);
00641     print("/", trace);
00642     print(iTotalLength, trace);
00643     print("\r\n", trace);
00644   }
00645   print("\r\n", trace);
00646   exit_();
00647 
00648 }
00649 
00650 void CSHTTPOutputStream::clearBody()
00651 {
00652   iRangeSize = 0;
00653   iRangeOffset = 0;
00654   iTotalLength = 0;
00655   iContentLength = 0;
00656   iBody.clear();
00657 }
00658 
00659 void CSHTTPOutputStream::writeBody()
00660 {
00661   iOutput->write(iBody.getBuffer(0), iBody.length());
00662 }
00663 
00664 void CSHTTPOutputStream::appendBody(const char *str)
00665 {
00666   iBody.append(str);
00667   iContentLength = iBody.length();
00668 }
00669 
00670 void CSHTTPOutputStream::appendBody(int32_t value)
00671 {
00672   iBody.append(value);
00673   iContentLength = iBody.length();
00674 }
00675 
00676 void CSHTTPOutputStream::appendBody(uint32_t value)
00677 {
00678   iBody.append(value);
00679   iContentLength = iBody.length();
00680 }
00681 
00682 void CSHTTPOutputStream::appendBody(uint64_t value)
00683 {
00684   iBody.append(value);
00685   iContentLength = iBody.length();
00686 }
00687 
00688 const char *CSHTTPOutputStream::getBodyData()
00689 {
00690   return iBody.getCString(); 
00691 }
00692 
00693 size_t CSHTTPOutputStream::getBodyLength()
00694 {
00695   return iBody.length();
00696 }
00697 
00698 void CSHTTPOutputStream::setBody(CSStringBufferImpl *buf)
00699 {
00700   iBody.take(buf);
00701   iContentLength = iBody.length(); 
00702 }
00703 
00704 void CSHTTPOutputStream::close()
00705 {
00706   enter_();
00707   iOutput->close();
00708   exit_();
00709 }
00710 
00711 void CSHTTPOutputStream::write(const char *b, size_t len)
00712 {
00713   enter_();
00714   iOutput->write(b, len);
00715   exit_();
00716 }
00717 
00718 void CSHTTPOutputStream::flush()
00719 {
00720   enter_();
00721   iOutput->flush();
00722   exit_();
00723 }
00724 
00725 void CSHTTPOutputStream::write(char b)
00726 {
00727   enter_();
00728   iOutput->write(b);
00729   exit_();
00730 }
00731 
00732 const char *CSHTTPOutputStream::getReasonPhrase(int code)
00733 {
00734   const char *message = "Unknown Code";
00735 
00736   switch (code) {
00737     case 100: message = "Continue"; break;
00738     case 101: message = "Switching Protocols"; break;
00739     case 200: message = "OK"; break;
00740     case 201: message = "Created"; break;
00741     case 202: message = "Accepted"; break;
00742     case 203: message = "Non-Authoritative Information"; break;
00743     case 204: message = "No Content"; break;
00744     case 205: message = "Reset Content"; break;
00745     case 206: message = "Partial Content"; break;
00746     case 300: message = "Multiple Choices"; break;
00747     case 301: message = "Moved Permanently"; break;
00748     case 302: message = "Found"; break;
00749     case 303: message = "See Other"; break;
00750     case 304: message = "Not Modified"; break;
00751     case 305: message = "Use Proxy"; break;
00752     case 307: message = "Temporary Redirect"; break;
00753     case 400: message = "Bad Request"; break;
00754     case 401: message = "Unauthorized"; break;
00755     case 402: message = "Payment Required"; break;
00756     case 403: message = "Forbidden"; break;
00757     case 404: message = "Not Found"; break;
00758     case 405: message = "Method Not Allowed"; break;
00759     case 406: message = "Not Acceptable"; break;
00760     case 407: message = "Proxy Authentication Required"; break;
00761     case 408: message = "Request Time-out"; break;
00762     case 409: message = "Conflict"; break;
00763     case 410: message = "Gone"; break;
00764     case 411: message = "Length Required"; break;
00765     case 412: message = "Precondition Failed"; break;
00766     case 413: message = "Request Entity Too Large"; break;
00767     case 414: message = "Request-URI Too Large"; break;
00768     case 415: message = "Unsupported Media Type"; break;
00769     case 416: message = "Requested range not satisfiable"; break;
00770     case 417: message = "Expectation Failed"; break;
00771     case 500: message = "Internal Server Error"; break;
00772     case 501: message = "Not Implemented"; break;
00773     case 502: message = "Bad Gateway"; break;
00774     case 503: message = "Service Unavailable"; break;
00775     case 504: message = "Gateway Time-out"; break;
00776     case 505: message = "HTTP Version not supported"; break;
00777   }
00778   return message;
00779 }
00780 
00781 CSHTTPOutputStream *CSHTTPOutputStream::newStream(CSOutputStream* i)
00782 {
00783   CSHTTPOutputStream *s;
00784 
00785   if (!(s = new CSHTTPOutputStream(i))) {
00786     i->release();
00787     CSException::throwOSError(CS_CONTEXT, ENOMEM);
00788   }
00789   return s;
00790 }
00791 
00792