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.
 
 
 
 
 
 

631 lines
17 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
  4. //
  5. // See accompanying file COPYING.TXT file for licensing details.
  6. //
  7. ///////////////////////////////////////////////////////////////////////////////
  8. #define CPPCMS_SOURCE
  9. #include <cppcms/url_dispatcher.h>
  10. #include <cppcms/http_context.h>
  11. #include <cppcms/http_request.h>
  12. #include <cppcms/http_response.h>
  13. #include <cppcms/forwarder.h>
  14. #include "http_protocol.h"
  15. #include <cppcms/service.h>
  16. #include "service_impl.h"
  17. #include "cached_settings.h"
  18. #include <cppcms/json.h>
  19. #include "cgi_api.h"
  20. #include "multipart_parser.h"
  21. #include <cppcms/util.h>
  22. #include <scgi_header.h>
  23. #include <stdlib.h>
  24. #include <cppcms/config.h>
  25. #ifdef CPPCMS_USE_EXTERNAL_BOOST
  26. # include <boost/bind.hpp>
  27. #else // Internal Boost
  28. # include <cppcms_boost/bind.hpp>
  29. namespace boost = cppcms_boost;
  30. #endif
  31. #include <booster/log.h>
  32. #include <booster/aio/endpoint.h>
  33. #include <booster/aio/aio_category.h>
  34. #include <booster/aio/socket.h>
  35. #include <booster/aio/buffer.h>
  36. namespace cppcms { namespace impl { namespace cgi {
  37. //
  38. // Special forwarder from generic CGI to SCGI
  39. //
  40. struct connection::cgi_forwarder : public booster::enable_shared_from_this<connection::cgi_forwarder> {
  41. public:
  42. cgi_forwarder(booster::shared_ptr<connection> c,std::string ip,int port) :
  43. conn_(c),
  44. scgi_(c->get_io_service()),
  45. ep_(ip,port)
  46. {
  47. booster::aio::endpoint ep(ip,port);
  48. booster::system::error_code e;
  49. scgi_.open(ep.family(),e);
  50. if(e) { return; }
  51. }
  52. void async_run()
  53. {
  54. scgi_.async_connect(ep_,boost::bind(&cgi_forwarder::on_connected,shared_from_this(),_1));
  55. }
  56. private:
  57. void on_connected(booster::system::error_code const &e)
  58. {
  59. if(e) return;
  60. header_ = make_scgi_header(conn_->getenv(),0);
  61. scgi_.async_write(
  62. booster::aio::buffer(header_),
  63. boost::bind(&cgi_forwarder::on_header_sent,shared_from_this(),_1,_2));
  64. }
  65. void on_header_sent(booster::system::error_code const &e,size_t n)
  66. {
  67. if(e || n!=header_.size())
  68. return;
  69. header_.clear();
  70. std::string slen = conn_->getenv("CONTENT_LENGTH");
  71. content_length_ = slen.empty() ? 0LL : atoll(slen.c_str());
  72. if(content_length_ > 0) {
  73. post_.resize( content_length_ > 8192 ? 8192 : content_length_,0);
  74. write_post();
  75. }
  76. else {
  77. response_.resize(8192);
  78. read_response();
  79. }
  80. }
  81. void write_post()
  82. {
  83. if(content_length_ > 0) {
  84. if(content_length_ < (long long)(post_.size())) {
  85. post_.resize(content_length_);
  86. }
  87. conn_->async_read_some(&post_.front(),post_.size(),
  88. boost::bind(&cgi_forwarder::on_post_data_read,shared_from_this(),_1,_2));
  89. }
  90. else {
  91. response_.swap(post_);
  92. response_.resize(8192);
  93. read_response();
  94. }
  95. }
  96. void on_post_data_read(booster::system::error_code const &e,size_t len)
  97. {
  98. if(e) { cleanup(); return; }
  99. conn_->on_async_read_complete();
  100. scgi_.async_write(
  101. booster::aio::buffer(&post_.front(),len),
  102. boost::bind(&cgi_forwarder::on_post_data_written,shared_from_this(),_1,_2));
  103. }
  104. void on_post_data_written(booster::system::error_code const &e,size_t len)
  105. {
  106. if(e) { return; }
  107. content_length_ -= len;
  108. write_post();
  109. }
  110. void read_response()
  111. {
  112. conn_->async_read_eof(boost::bind(&cgi_forwarder::cleanup,shared_from_this()));
  113. scgi_.async_read_some(booster::aio::buffer(response_),
  114. boost::bind(&cgi_forwarder::on_response_read,shared_from_this(),_1,_2));
  115. }
  116. void on_response_read(booster::system::error_code const &e,size_t len)
  117. {
  118. if(e) {
  119. conn_->async_write(booster::aio::const_buffer(),true,boost::bind(&cgi_forwarder::cleanup,shared_from_this()));
  120. return;
  121. }
  122. else {
  123. conn_->async_write(booster::aio::buffer(&response_.front(),len),false,boost::bind(&cgi_forwarder::on_response_written,shared_from_this(),_1));
  124. }
  125. }
  126. void on_response_written(booster::system::error_code const &e)
  127. {
  128. if(e) { cleanup(); return; }
  129. scgi_.async_read_some(booster::aio::buffer(response_),
  130. boost::bind(&cgi_forwarder::on_response_read,shared_from_this(),_1,_2));
  131. }
  132. void cleanup()
  133. {
  134. conn_->do_eof();
  135. booster::system::error_code e;
  136. scgi_.shutdown(booster::aio::stream_socket::shut_rdwr,e);
  137. scgi_.close(e);
  138. }
  139. booster::shared_ptr<connection> conn_;
  140. booster::aio::stream_socket scgi_;
  141. booster::aio::endpoint ep_;
  142. long long int content_length_;
  143. std::string header_;
  144. std::vector<char> post_;
  145. std::vector<char> response_;
  146. };
  147. connection::connection(cppcms::service &srv) :
  148. service_(&srv),
  149. request_in_progress_(true)
  150. {
  151. }
  152. connection::~connection()
  153. {
  154. }
  155. cppcms::service &connection::service()
  156. {
  157. return *service_;
  158. }
  159. booster::shared_ptr<connection> connection::self()
  160. {
  161. return shared_from_this();
  162. }
  163. void connection::async_prepare_request( http::context *context,
  164. ehandler const &h)
  165. {
  166. async_read_headers(boost::bind(&connection::on_headers_read,self(),_1,context,h));
  167. }
  168. void connection::on_headers_read(booster::system::error_code const &e,http::context *context,ehandler const &h)
  169. {
  170. if(e) {
  171. set_error(h,e.message());
  172. return;
  173. }
  174. forwarder::address_type addr = service().forwarder().check_forwading_rules(
  175. cgetenv("HTTP_HOST"),
  176. cgetenv("SCRIPT_NAME"),
  177. cgetenv("PATH_INFO"));
  178. if(addr.second != 0 && !addr.first.empty()) {
  179. booster::shared_ptr<cgi_forwarder> f(new cgi_forwarder(self(),addr.first,addr.second));
  180. f->async_run();
  181. h(http::context::operation_aborted);
  182. return;
  183. }
  184. context->request().prepare();
  185. load_content(e,context,h);
  186. }
  187. void connection::aync_wait_for_close_by_peer(booster::callback<void()> const &on_eof)
  188. {
  189. async_read_eof(boost::bind(&connection::handle_eof,self(),on_eof));
  190. }
  191. void connection::handle_eof(callback const &on_eof)
  192. {
  193. if(request_in_progress_) {
  194. on_eof();
  195. }
  196. }
  197. void connection::set_error(ehandler const &h,std::string s)
  198. {
  199. error_=s;
  200. h(http::context::operation_aborted);
  201. }
  202. void connection::handle_http_error(int code,http::context *context,ehandler const &h)
  203. {
  204. async_chunk_.clear();
  205. async_chunk_.reserve(256);
  206. std::string status;
  207. status.reserve(128);
  208. status += char('0' + code/100);
  209. status += char('0' + code/10 % 10);
  210. status += char('0' + code % 10);
  211. status += ' ';
  212. status += http::response::status_to_string(code);
  213. if(context->service().cached_settings().service.generate_http_headers) {
  214. async_chunk_ += "HTTP/1.0 ";
  215. async_chunk_ += status;
  216. async_chunk_ += "\r\n"
  217. "Connection: close\r\n"
  218. "Content-Type: text/html\r\n"
  219. "\r\n";
  220. }
  221. else {
  222. async_chunk_ += "Content-Type: text/html\r\n"
  223. "Status: ";
  224. async_chunk_ += status;
  225. async_chunk_ += "\r\n"
  226. "\r\n";
  227. }
  228. async_chunk_ +=
  229. "<html>\r\n"
  230. "<body>\r\n"
  231. "<h1>";
  232. async_chunk_ += status;
  233. async_chunk_ += "</h1>\r\n"
  234. "</body>\r\n"
  235. "</html>\r\n";
  236. async_write(booster::aio::buffer(async_chunk_),true,
  237. boost::bind(
  238. &connection::handle_http_error_eof,
  239. self(),
  240. _1,
  241. code,
  242. h));
  243. }
  244. void connection::handle_http_error_eof(
  245. booster::system::error_code const &e,
  246. int code,
  247. ehandler const &h)
  248. {
  249. if(e) {
  250. set_error(h,e.message());
  251. return;
  252. }
  253. do_eof();
  254. set_error(h,http::response::status_to_string(code));
  255. }
  256. void connection::load_content(booster::system::error_code const &e,http::context *context,ehandler const &h)
  257. {
  258. if(e) {
  259. set_error(h,e.message());
  260. return;
  261. }
  262. http::content_type content_type = context->request().content_type_parsed();
  263. char const *s_content_length=cgetenv("CONTENT_LENGTH");
  264. long long content_length = *s_content_length == 0 ? 0 : atoll(s_content_length);
  265. if(content_length < 0) {
  266. handle_http_error(400,context,h);
  267. return;
  268. }
  269. if(content_length > 0) {
  270. if(content_type.media_type()=="multipart/form-data") {
  271. // 64 MB
  272. long long allowed=service().cached_settings().security.multipart_form_data_limit*1024;
  273. if(content_length > allowed) {
  274. BOOSTER_NOTICE("cppcms") << "multipart/form-data size too big " << content_length <<
  275. " REMOTE_ADDR = `" << getenv("REMOTE_ADDR") << "' REMOTE_HOST=`" << getenv("REMOTE_HOST") << "'";
  276. handle_http_error(413,context,h);
  277. return;
  278. }
  279. multipart_parser_.reset(new multipart_parser(
  280. service().cached_settings().security.uploads_path,
  281. service().cached_settings().security.file_in_memory_limit));
  282. read_size_ = content_length;
  283. if(!multipart_parser_->set_content_type(content_type)) {
  284. BOOSTER_NOTICE("cppcms") << "Invalid multipart/form-data request" << content_length <<
  285. " REMOTE_ADDR = `" << getenv("REMOTE_ADDR") << "' REMOTE_HOST=`" << getenv("REMOTE_HOST") << "'";
  286. handle_http_error(400,context,h);
  287. return;
  288. }
  289. content_.clear();
  290. content_.resize(8192);
  291. async_read_some(&content_.front(),content_.size(),
  292. boost::bind(&connection::on_some_multipart_read,
  293. self(),
  294. _1,
  295. _2,
  296. context,
  297. h));
  298. }
  299. else {
  300. long long allowed=service().cached_settings().security.content_length_limit*1024;
  301. if(content_length > allowed) {
  302. BOOSTER_NOTICE("cppcms") << "POST data size too big " << content_length <<
  303. " REMOTE_ADDR = `" << getenv("REMOTE_ADDR") << "' REMOTE_HOST=`" << getenv("REMOTE_HOST") << "'";
  304. handle_http_error(413,context,h);
  305. return;
  306. }
  307. content_.clear();
  308. content_.resize(content_length,0);
  309. async_read( &content_.front(),
  310. content_.size(),
  311. boost::bind(&connection::on_post_data_loaded,self(),_1,context,h));
  312. }
  313. }
  314. else {
  315. on_post_data_loaded(booster::system::error_code(),context,h);
  316. }
  317. }
  318. void connection::on_some_multipart_read(booster::system::error_code const &e,size_t n,http::context *context,ehandler const &h)
  319. {
  320. if(e) { set_error(h,e.message()); return; }
  321. read_size_-=n;
  322. if(read_size_ < 0) { handle_http_error(400,context,h); return ;}
  323. multipart_parser::parsing_result_type r = multipart_parser_->consume(&content_.front(),n);
  324. if(r == multipart_parser::eof) {
  325. if(read_size_ != 0) {
  326. handle_http_error(400,context,h);
  327. return;
  328. }
  329. content_.clear();
  330. multipart_parser::files_type files = multipart_parser_->get_files();
  331. long long allowed=service().cached_settings().security.content_length_limit*1024;
  332. for(unsigned i=0;i<files.size();i++) {
  333. if(files[i]->mime().empty() && files[i]->size() > allowed) {
  334. BOOSTER_NOTICE("cppcms") << "multipart/form-data non-file entry size too big " <<
  335. files[i]->size()
  336. << " REMOTE_ADDR = `" << getenv("REMOTE_ADDR")
  337. << "' REMOTE_HOST=`" << getenv("REMOTE_HOST") << "'";
  338. handle_http_error(413,context,h);
  339. return;
  340. }
  341. }
  342. context->request().set_post_data(files);
  343. multipart_parser_.reset();
  344. h(http::context::operation_completed);
  345. return;
  346. }
  347. else if (r==multipart_parser::parsing_error) {
  348. handle_http_error(400,context,h);
  349. return;
  350. }
  351. else if(r==multipart_parser::no_room_left) {
  352. handle_http_error(413,context,h);
  353. return;
  354. }
  355. else if(read_size_ == 0) {
  356. handle_http_error(400,context,h);
  357. return;
  358. }
  359. else {
  360. async_read_some(&content_.front(),content_.size(),
  361. boost::bind(&connection::on_some_multipart_read,
  362. self(),
  363. _1,
  364. _2,
  365. context,
  366. h));
  367. }
  368. }
  369. void connection::on_post_data_loaded(booster::system::error_code const &e,http::context *context,ehandler const &h)
  370. {
  371. if(e) { set_error(h,e.message()); return; }
  372. context->request().set_post_data(content_);
  373. on_async_read_complete();
  374. h(http::context::operation_completed);
  375. }
  376. bool connection::is_reuseable()
  377. {
  378. return error_.empty() && keep_alive();
  379. }
  380. std::string connection::last_error()
  381. {
  382. return error_;
  383. }
  384. struct connection::async_write_binder : public booster::callable<void(booster::system::error_code const &)> {
  385. typedef booster::shared_ptr<cppcms::impl::cgi::connection> conn_type;
  386. conn_type conn;
  387. ehandler h;
  388. bool complete_response;
  389. void reset()
  390. {
  391. h=ehandler();
  392. conn.reset();
  393. complete_response = false;
  394. }
  395. void operator()(booster::system::error_code const &e)
  396. {
  397. if(complete_response) {
  398. conn->do_eof();
  399. }
  400. h(e ? cppcms::http::context::operation_aborted : cppcms::http::context::operation_completed );
  401. if(!conn->cached_async_write_binder_) {
  402. conn->cached_async_write_binder_ = this;
  403. reset();
  404. }
  405. }
  406. };
  407. void connection::async_write_response( http::response &response,
  408. bool complete_response,
  409. ehandler const &h)
  410. {
  411. // prepare cached binder
  412. booster::intrusive_ptr<connection::async_write_binder> tmp;
  413. if(cached_async_write_binder_) {
  414. tmp.swap(cached_async_write_binder_);
  415. }
  416. if(!tmp) {
  417. tmp = new connection::async_write_binder();
  418. }
  419. tmp->conn = self();
  420. tmp->h = h;
  421. tmp->complete_response = complete_response;
  422. // ready
  423. booster::system::error_code e;
  424. if(response.flush_async_chunk(e)!=0 || !has_pending()) {
  425. get_io_service().post(tmp,e);
  426. return;
  427. }
  428. async_write(booster::aio::const_buffer(),false,tmp);
  429. }
  430. bool connection::has_pending()
  431. {
  432. return !pending_output_.empty();
  433. }
  434. void connection::append_pending(booster::aio::const_buffer const &new_data)
  435. {
  436. size_t pos = pending_output_.size();
  437. pending_output_.resize(pending_output_.size() + new_data.bytes_count());
  438. std::pair<booster::aio::const_buffer::entry const *,size_t> packets = new_data.get();
  439. for(size_t i=0;i<packets.second;i++) {
  440. memcpy(&pending_output_[pos],packets.first[i].ptr,packets.first[i].size);
  441. pos += packets.first[i].size;
  442. }
  443. }
  444. bool connection::write(booster::aio::const_buffer const &buf,bool eof,booster::system::error_code &e)
  445. {
  446. booster::aio::const_buffer new_data = format_output(buf,eof,e);
  447. if(e) return false;
  448. booster::aio::const_buffer output = pending_output_.empty() ? new_data : (booster::aio::buffer(pending_output_) + new_data);
  449. if(output.empty())
  450. return true;
  451. socket().set_non_blocking_if_needed(false,e);
  452. if(e) return false;
  453. bool r=write_to_socket(output,e);
  454. pending_output_.clear();
  455. return r;
  456. }
  457. bool connection::write_to_socket(booster::aio::const_buffer const &in,booster::system::error_code &e)
  458. {
  459. return socket().write(in,e) == in.bytes_count();
  460. }
  461. bool connection::nonblocking_write(booster::aio::const_buffer const &buf,bool eof,booster::system::error_code &e)
  462. {
  463. booster::aio::const_buffer new_data = format_output(buf,eof,e);
  464. if(e) return false;
  465. booster::aio::const_buffer output = pending_output_.empty() ? new_data : (booster::aio::buffer(pending_output_) + new_data);
  466. if(output.empty())
  467. return true;
  468. socket().set_non_blocking_if_needed(true,e);
  469. if(e) return false;
  470. size_t n = socket().write_some(output,e);
  471. if(n == output.bytes_count()) {
  472. pending_output_.clear();
  473. return true;
  474. }
  475. if(n == 0) {
  476. append_pending(new_data);
  477. }
  478. else {
  479. std::vector<char> tmp;
  480. pending_output_.swap(tmp);
  481. // after swapping output still points to a valid buffer
  482. append_pending(output + n);
  483. }
  484. if(e && socket().would_block(e)) {
  485. e=booster::system::error_code();
  486. return false;
  487. }
  488. return false;
  489. }
  490. struct connection::async_write_handler : public booster::callable<void(booster::system::error_code const &e)>
  491. {
  492. typedef booster::shared_ptr<cppcms::impl::cgi::connection> conn_type;
  493. typedef booster::intrusive_ptr< booster::callable<void(booster::system::error_code const &)> > self_type;
  494. std::vector<char> data;
  495. booster::aio::const_buffer output;
  496. handler h;
  497. conn_type conn;
  498. async_write_handler(conn_type const &c,std::vector<char> &d,handler const &hin) :
  499. h(hin),
  500. conn(c)
  501. {
  502. data.swap(d);
  503. output = booster::aio::buffer(data);
  504. }
  505. virtual void operator()(booster::system::error_code const &ein)
  506. {
  507. if(ein) { h(ein); return; }
  508. booster::system::error_code e;
  509. conn->socket().set_non_blocking_if_needed(true,e);
  510. size_t n = conn->socket().write_some(output,e);
  511. output += n;
  512. if(n!=0) {
  513. conn->on_async_write_progress(output.empty());
  514. }
  515. if(output.empty()) {
  516. h(e);
  517. return;
  518. }
  519. if(e && booster::aio::basic_io_device::would_block(e)) {
  520. conn->socket().on_writeable(self_type(this));
  521. return;
  522. }
  523. }
  524. };
  525. void connection::async_write(booster::aio::const_buffer const &buf,bool eof,handler const &h)
  526. {
  527. booster::system::error_code e;
  528. if(nonblocking_write(buf,eof,e) || e) {
  529. get_io_service().post(h,e);
  530. return;
  531. }
  532. on_async_write_start();
  533. async_write_handler::self_type p(new async_write_handler(self(),pending_output_,h));
  534. socket().on_writeable(p);
  535. }
  536. struct connection::reader {
  537. reader(connection *C,io_handler const &H,size_t S,char *P) : h(H), s(S), p(P),conn(C)
  538. {
  539. done=0;
  540. }
  541. io_handler h;
  542. size_t s;
  543. size_t done;
  544. char *p;
  545. connection *conn;
  546. void operator() (booster::system::error_code const &e=booster::system::error_code(),size_t read = 0)
  547. {
  548. if(e) {
  549. h(e,done+read);
  550. return;
  551. }
  552. s-=read;
  553. p+=read;
  554. done+=read;
  555. if(s==0)
  556. h(booster::system::error_code(),done);
  557. else
  558. conn->async_read_some(p,s,*this);
  559. }
  560. };
  561. void connection::async_read(void *p,size_t s,io_handler const &h)
  562. {
  563. reader r(this,h,s,(char*)p);
  564. r();
  565. }
  566. } // cgi
  567. } // impl
  568. } // cppcms