ChipMaster's trial hacks on C++CMS starting with v1.2.1. Not sure I'll follow on with the v2 since it looks to be breaking and mostly frivolous.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

425 lines
12 KiB

  1. #define CPPCMS_SOURCE
  2. #include "cgi_api.h"
  3. #include "http_response.h"
  4. #include "http_context.h"
  5. #include "http_request.h"
  6. #include "http_cookie.h"
  7. #include "http_protocol.h"
  8. #include "json.h"
  9. #include "cppcms_error.h"
  10. #include "service.h"
  11. #include "config.h"
  12. #include "localization.h"
  13. #include "util.h"
  14. #include "session_interface.h"
  15. #include <iostream>
  16. #include <sstream>
  17. #include <iterator>
  18. #include <map>
  19. #ifdef CPPCMS_USE_EXTERNAL_BOOST
  20. # include <boost/lexical_cast.hpp>
  21. # include <boost/iostreams/stream.hpp>
  22. # include <boost/iostreams/filtering_stream.hpp>
  23. # include <boost/iostreams/filter/gzip.hpp>
  24. # include <boost/iostreams/tee.hpp>
  25. #else // Internal Boost
  26. # include <cppcms_boost/lexical_cast.hpp>
  27. # include <cppcms_boost/iostreams/stream.hpp>
  28. # include <cppcms_boost/iostreams/filtering_stream.hpp>
  29. # include <cppcms_boost/iostreams/filter/gzip.hpp>
  30. # include <cppcms_boost/iostreams/tee.hpp>
  31. namespace boost = cppcms_boost;
  32. #endif
  33. #ifndef HAVE_GMTIME_R
  34. #ifdef CPPCMS_USE_EXTERNAL_BOOST
  35. # include <boost/date_time/posix_time/posix_time.hpp>
  36. #else // Internal Boost
  37. # include <cppcms_boost/date_time/posix_time/posix_time.hpp>
  38. #endif
  39. #endif
  40. namespace cppcms { namespace http {
  41. namespace {
  42. bool string_i_comp(std::string const &left,std::string const &right)
  43. {
  44. return http::protocol::compare(left,right) < 0;
  45. }
  46. } // anon
  47. namespace {
  48. class output_device : public boost::iostreams::sink {
  49. impl::cgi::connection *conn_;
  50. public:
  51. output_device(impl::cgi::connection *conn) : conn_(conn) {}
  52. std::streamsize write(char const *data,std::streamsize n)
  53. {
  54. if(n==0)
  55. return 0;
  56. return conn_->write(data,n);
  57. }
  58. };
  59. }
  60. struct response::data {
  61. typedef bool (*compare_type)(std::string const &left,std::string const &right);
  62. typedef std::map<std::string,std::string,compare_type> headers_type;
  63. headers_type headers;
  64. std::vector<cookie> cookies;
  65. std::ostringstream cached;
  66. std::ostringstream buffered;
  67. boost::iostreams::stream<output_device> output;
  68. boost::iostreams::filtering_ostream filter;
  69. data(impl::cgi::connection *conn) :
  70. headers(string_i_comp),
  71. output(output_device(conn),conn->service().settings().get("service.output_buffer_size",16384))
  72. {
  73. }
  74. };
  75. response::response(context &context) :
  76. d(new data(&context.connection())),
  77. context_(context),
  78. stream_(0),
  79. io_mode_(normal),
  80. disable_compression_(0),
  81. ostream_requested_(0),
  82. copy_to_cache_(0),
  83. finalized_(0)
  84. {
  85. set_content_header("text/html");
  86. if(context_.settings().get("server.disable_xpowered_by",false)==0) {
  87. set_header("X-Powered-By", PACKAGE_NAME "/" PACKAGE_VERSION);
  88. }
  89. }
  90. response::~response()
  91. {
  92. }
  93. void response::set_content_header(std::string const &content_type)
  94. {
  95. if(context_.settings().get("localization.disable_charset_in_content_type",false)) {
  96. set_header("Content-Type",content_type);
  97. }
  98. else {
  99. std::string charset=std::use_facet<locale::info>(context_.locale()).encoding();
  100. set_header("Content-Type",content_type+"; charset="+charset);
  101. }
  102. }
  103. void response::set_html_header()
  104. {
  105. set_content_header("text/html");
  106. }
  107. void response::set_xhtml_header()
  108. {
  109. set_content_header("text/xhtml");
  110. }
  111. void response::set_plain_text_header()
  112. {
  113. set_content_header("text/plain");
  114. }
  115. void response::set_redirect_header(std::string const &loc,int s)
  116. {
  117. location(loc);
  118. status(s);
  119. }
  120. void response::set_cookie(cookie const &cookie)
  121. {
  122. d->cookies.push_back(cookie);
  123. }
  124. void response::set_header(std::string const &name,std::string const &value)
  125. {
  126. if(value.empty())
  127. d->headers.erase(name);
  128. else
  129. d->headers[name]=value;
  130. }
  131. void response::finalize()
  132. {
  133. if(!finalized_) {
  134. out()<<std::flush;
  135. d->filter.reset();
  136. finalized_=1;
  137. }
  138. }
  139. std::string response::get_header(std::string const &name)
  140. {
  141. data::headers_type::const_iterator p=d->headers.find(name);
  142. if(p!=d->headers.end())
  143. return p->second;
  144. return std::string();
  145. }
  146. void response::erase_header(std::string const &name)
  147. {
  148. d->headers.erase(name);
  149. }
  150. bool response::need_gzip()
  151. {
  152. if(disable_compression_)
  153. return false;
  154. if(io_mode_!=normal)
  155. return false;
  156. if(context_.settings().get("gzip.enable",true)==0)
  157. return false;
  158. if(context_.request().http_accept_encoding().find("gzip")==std::string::npos)
  159. return false;
  160. if(!get_header("Content-Encoding").empty())
  161. // User had defined its own content encoding
  162. // he may compress data on its own... disable compression
  163. return false;
  164. std::string const content_type=get_header("Content-Type");
  165. if(protocol::is_prefix_of("text/",content_type))
  166. return true;
  167. return false;
  168. }
  169. response::io_mode_type response::io_mode()
  170. {
  171. return io_mode_;
  172. }
  173. void response::io_mode(response::io_mode_type mode)
  174. {
  175. if(ostream_requested_)
  176. throw cppcms_error("Can't set mode after requesting output stream");
  177. io_mode_=mode;
  178. }
  179. void response::write_http_headers(std::ostream &out)
  180. {
  181. context_.session().save();
  182. for(data::headers_type::const_iterator h=d->headers.begin();h!=d->headers.end();++h) {
  183. out<<h->first<<": "<<h->second<<"\r\n";
  184. }
  185. for(unsigned i=0;i<d->cookies.size();i++) {
  186. out<<d->cookies[i]<<"\r\n";
  187. }
  188. out<<"\r\n";
  189. out<<std::flush;
  190. }
  191. void response::copy_to_cache()
  192. {
  193. copy_to_cache_=1;
  194. }
  195. std::string response::copied_data()
  196. {
  197. if(!copy_to_cache_ || !ostream_requested_)
  198. return std::string();
  199. return d->cached.str();
  200. }
  201. std::ostream &response::out()
  202. {
  203. using namespace boost::iostreams;
  204. if(ostream_requested_)
  205. return *stream_;
  206. if(finalized_)
  207. throw cppcms_error("Request for output stream for finalized request is illegal");
  208. ostream_requested_=1;
  209. std::ostream *real_sink = 0;
  210. if(io_mode_ == asynchronous || io_mode_ == asynchronous_raw)
  211. real_sink = &d->buffered;
  212. else
  213. real_sink = &d->output;
  214. bool gzip = need_gzip();
  215. if(gzip) {
  216. content_encoding("gzip");
  217. }
  218. // Now we shoulde write headers -- before comrpession
  219. if(io_mode_ != raw && io_mode_ != asynchronous_raw)
  220. write_http_headers(*real_sink);
  221. if(gzip) {
  222. gzip_params params;
  223. int level=context_.settings().get("gzip.level",-1);
  224. if(level!=-1)
  225. params.level=level;
  226. int buffer=context_.settings().get("gzip.buffer",-1);
  227. if(buffer!=-1)
  228. d->filter.push(gzip_compressor(params,buffer));
  229. else
  230. d->filter.push(gzip_compressor(params));
  231. stream_ = &d->filter;
  232. }
  233. if(copy_to_cache_) {
  234. d->filter.push(tee_filter<std::ostream>(d->cached));
  235. stream_ = &d->filter;
  236. }
  237. if(stream_)
  238. d->filter.push(*real_sink);
  239. else
  240. stream_=real_sink;
  241. stream_->imbue(context_.locale());
  242. return *stream_;
  243. }
  244. std::string response::get_async_chunk()
  245. {
  246. std::string result=d->buffered.str();
  247. d->buffered.str("");
  248. return result;
  249. }
  250. bool response::some_output_was_written()
  251. {
  252. return ostream_requested_;
  253. }
  254. char const *response::status_to_string(int status)
  255. {
  256. switch(status) {
  257. case 100: return "Continue";
  258. case 101: return "Switching Protocols";
  259. case 200: return "OK";
  260. case 201: return "Created";
  261. case 202: return "Accepted";
  262. case 203: return "Non-Authoritative Information";
  263. case 204: return "No Content";
  264. case 205: return "Reset Content";
  265. case 206: return "Partial Content";
  266. case 300: return "Multiple Choices";
  267. case 301: return "Moved Permanently";
  268. case 302: return "Found";
  269. case 303: return "See Other";
  270. case 304: return "Not Modified";
  271. case 305: return "Use Proxy";
  272. case 307: return "Temporary Redirect";
  273. case 400: return "Bad Request";
  274. case 401: return "Unauthorized";
  275. case 402: return "Payment Required";
  276. case 403: return "Forbidden";
  277. case 404: return "Not Found";
  278. case 405: return "Method Not Allowed";
  279. case 406: return "Not Acceptable";
  280. case 407: return "Proxy Authentication Required";
  281. case 408: return "Request Time-out";
  282. case 409: return "Conflict";
  283. case 410: return "Gone";
  284. case 411: return "Length Required";
  285. case 412: return "Precondition Failed";
  286. case 413: return "Request Entity Too Large";
  287. case 414: return "Request-URI Too Large";
  288. case 415: return "Unsupported Media Type";
  289. case 416: return "Requested range not satisfiable";
  290. case 417: return "Expectation Failed";
  291. case 500: return "Internal Server Error";
  292. case 501: return "Not Implemented";
  293. case 502: return "Bad Gateway";
  294. case 503: return "Service Unavailable";
  295. case 504: return "Gateway Time-out";
  296. case 505: return "HTTP Version not supported";
  297. default: return "Unknown";
  298. }
  299. }
  300. void response::make_error_response(int stat,std::string const &msg)
  301. {
  302. status(stat);
  303. out() <<"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
  304. " \"http://www.w3.org/TR/html4/loose.dtd\">\n"
  305. "<html>\n"
  306. " <head>\n"
  307. " <title>"<<stat<<" &mdash; "<< http::response::status_to_string(stat)<<"</title>\n"
  308. " </head>\n"
  309. " <body>\n"
  310. " <h1>"<<stat<<" &mdash; "<< http::response::status_to_string(stat)<<"</h1>\n";
  311. if(!msg.empty()) {
  312. out()<<" <p>"<<util::escape(msg)<<"</p>\n";
  313. }
  314. out()<< " </body>\n"
  315. "</html>\n"<<std::flush;
  316. }
  317. void response::accept_ranges(std::string const &s) { set_header("Accept-Ranges",s); }
  318. void response::age(unsigned seconds) { set_header("Age",boost::lexical_cast<std::string>(seconds)); }
  319. void response::allow(std::string const &s) { set_header("Allow",s); }
  320. void response::cache_control(std::string const &s) { set_header("Cache-Control",s); }
  321. void response::content_encoding(std::string const &s) { set_header("Content-Encoding",s); }
  322. void response::content_language(std::string const &s) { set_header("Content-Language",s); }
  323. void response::content_length(unsigned long long len)
  324. {
  325. set_header("Content-Length",boost::lexical_cast<std::string>(len));
  326. }
  327. void response::content_location(std::string const &s) { set_header("Content-Locaton",s); }
  328. void response::content_md5(std::string const &s) { set_header("Content-MD5",s); }
  329. void response::content_range(std::string const &s) { set_header("Content-Range",s); }
  330. void response::content_type(std::string const &s) { set_header("Content-Type",s); }
  331. void response::date(time_t t) { set_header("Date",make_http_time(t)); }
  332. void response::etag(std::string const &s) { set_header("ETag",s); }
  333. void response::expires(time_t t) { set_header("Expires",make_http_time(t)); }
  334. void response::last_modified(time_t t) { set_header("Last-Modified",make_http_time(t)); }
  335. void response::location(std::string const &s) { set_header("Location",s); }
  336. void response::pragma(std::string const &s) { set_header("Pragma",s); }
  337. void response::proxy_authenticate(std::string const &s) { set_header("Proxy-Authenticate",s); }
  338. void response::retry_after(unsigned n) { set_header("Retry-After",boost::lexical_cast<std::string>(n)); }
  339. void response::retry_after(std::string const &s) { set_header("Retry-After",s); }
  340. void response::status(int code)
  341. {
  342. status(code,status_to_string(code));
  343. }
  344. void response::status(int code,std::string const &message)
  345. {
  346. set_header("Status",boost::lexical_cast<std::string>(code)+" "+message);
  347. }
  348. void response::trailer(std::string const &s) { set_header("Trailer",s); }
  349. void response::transfer_encoding(std::string const &s) { set_header("Transfer-Encoding",s); }
  350. void response::vary(std::string const &s) { set_header("Vary",s); }
  351. void response::via(std::string const &s) { set_header("Via",s); }
  352. void response::warning(std::string const &s) { set_header("Warning",s); }
  353. void response::www_authenticate(std::string const &s) { set_header("WWW-Authenticate",s); }
  354. std::string response::make_http_time(time_t t)
  355. {
  356. // RFC 2616
  357. // "Sun, 06 Nov 1994 08:49:37 GMT"
  358. std::tm tv;
  359. #ifdef HAVE_GMTIME_R
  360. gmtime_r(&t,&tv);
  361. #else
  362. using namespace boost::posix_time;
  363. tv=to_tm(from_time_t(t));
  364. #endif
  365. std::ostringstream ss;
  366. std::locale C("C");
  367. ss.imbue(C);
  368. std::time_put<char> const &put = std::use_facet<std::time_put<char> >(C);
  369. char const format[]="%a, %d %b %Y %H:%M:%S GMT";
  370. put.put(ss,ss,' ',&tv,format,format+sizeof(format)-1);
  371. return ss.str();
  372. }
  373. } } // http::cppcms