PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/boost/cgi/fcgi/specification.hpp

http://github.com/darrengarvey/cgi
C++ Header | 619 lines | 467 code | 88 blank | 64 comment | 0 complexity | f2782ab2edbd7a542ca80bd95f1f559e MD5 | raw file
  1. // -- fcgi/specification.hpp --
  2. //
  3. // Copyright (c) Darren Garvey 2007.
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. ////////////////////////////////////////////////////////////////
  9. #ifndef CGI_FCGI_SPECIFICATION_HPP_INCLUDED__
  10. #define CGI_FCGI_SPECIFICATION_HPP_INCLUDED__
  11. #include <algorithm>
  12. #include <boost/mpl/int.hpp>
  13. #include <boost/array.hpp>
  14. #include <boost/cstdint.hpp>
  15. #include <boost/asio/buffer.hpp>
  16. #include "boost/cgi/config.hpp"
  17. // NOTE: CamelCase style mimicks the FastCGI specification
  18. // SEE: http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S8
  19. BOOST_CGI_NAMESPACE_BEGIN
  20. namespace fcgi {
  21. namespace spec_detail {
  22. // Listening socket file number
  23. const short LISTENSOCK_FILENO = 0;
  24. /**
  25. * Number of bytes in a Header. Future versions of the protocol
  26. * will not reduce this number
  27. */
  28. const short HEADER_LEN = 8;
  29. #if 1
  30. // Value for version component of Header
  31. const short BOOST_CGI_FASTCGI_VERSION_1 = 1;
  32. // current version
  33. const unsigned char VERSION_NUM
  34. = (unsigned char)BOOST_CGI_FASTCGI_VERSION_1;
  35. // Values for the type component of Header
  36. enum request_types
  37. { BEGIN_REQUEST = 1
  38. , ABORT_REQUEST = 2
  39. , END_REQUEST = 3
  40. , PARAMS = 4
  41. , STDIN = 5
  42. , STDOUT = 6
  43. , STDERR = 7
  44. , DATA = 8
  45. , GET_VALUES = 9
  46. , GET_VALUES_RESULT = 10
  47. , UNKNOWN_TYPE = 11
  48. , MAXTYPE = UNKNOWN_TYPE
  49. };
  50. // a null request id is a management record
  51. static const unsigned char NULL_REQUEST_ID = 0;
  52. // Mask for flags component of BeginRequestBody
  53. static const unsigned char KEEP_CONN = 1;
  54. // The longest message possible per record
  55. const boost::uint16_t MAX_MSG_LEN = 65535;
  56. // Values for role component of BeginRequestBody
  57. enum role_types {
  58. NONE = 0
  59. , RESPONDER = 1
  60. , AUTHORIZER = 2
  61. , FILTER = 3
  62. };
  63. // Values for protocolStatus component of EndRequestBody
  64. enum status_types { REQUEST_COMPLETE = 0
  65. , CANT_MPX_CONN = 1
  66. , OVERLOADED = 2
  67. , UNKNOWN_ROLE = 3
  68. };
  69. struct Header
  70. {
  71. //private:
  72. /// The underlying type of a FastCGI header.
  73. /**
  74. * To guarantee the header is laid out exactly as we want, the
  75. * structure must be a POD-type (see http://tinyurl.com/yo9eav).
  76. */
  77. struct implementation_type
  78. {
  79. unsigned char version_;
  80. unsigned char type_;
  81. unsigned char requestIdB1_;
  82. unsigned char requestIdB0_;
  83. unsigned char contentLengthB1_;
  84. unsigned char contentLengthB0_;
  85. unsigned char paddingLength_;
  86. unsigned char reserved_;
  87. } impl;
  88. public:
  89. typedef boost::asio::const_buffers_1 const_buffers_type;
  90. typedef boost::asio::mutable_buffers_1 mutable_buffers_type;
  91. Header()
  92. {
  93. memset(static_cast<void*>(&this->impl)
  94. , 0, sizeof(this->impl));
  95. }
  96. Header(request_types t, int id, int len)
  97. {
  98. reset(t, id, len);
  99. }
  100. mutable_buffers_type data()
  101. {
  102. return boost::asio::buffer(
  103. static_cast<void*>(&impl)
  104. , sizeof(impl));
  105. }
  106. const_buffers_type data() const
  107. {
  108. return boost::asio::buffer(
  109. static_cast<const void*>(&impl)
  110. , sizeof(impl));
  111. }
  112. void reset(request_types t, int id, std::size_t len)
  113. {
  114. impl.version_ = (VERSION_NUM);
  115. impl.type_ = (unsigned char) (t);
  116. impl.requestIdB1_ = (unsigned char) ((id >> 8) & 0xff);
  117. impl.requestIdB0_ = (unsigned char) ((id ) & 0xff);
  118. impl.contentLengthB1_ = (unsigned char) ((boost::int32_t(len) >> 8) & 0xff);
  119. impl.contentLengthB0_ = (unsigned char) (boost::int32_t(len) & 0xff);
  120. impl.paddingLength_ = (unsigned char) (0);
  121. impl.reserved_ = (0);
  122. }
  123. boost::uint16_t version() const
  124. {
  125. return impl.version_;
  126. }
  127. boost::uint16_t type() const
  128. {
  129. return impl.type_;
  130. }
  131. boost::uint16_t request_id() const
  132. {
  133. return impl.requestIdB0_ + (impl.requestIdB1_ << 8);
  134. }
  135. boost::uint16_t content_length() const
  136. {
  137. return impl.contentLengthB0_ + (impl.contentLengthB1_ << 8);
  138. }
  139. boost::uint16_t padding_length() const
  140. {
  141. return impl.paddingLength_;
  142. }
  143. int body_length() const
  144. {
  145. return content_length() + padding_length();
  146. }
  147. };
  148. class BeginRequestBody
  149. {
  150. /// The underlying type of a BeginRequestBody sub-header.
  151. /**
  152. * To guarantee the header is laid out exactly as we want, the
  153. * structure must be a POD-type (see http://tinyurl.com/yo9eav).
  154. */
  155. struct implementation_type
  156. {
  157. unsigned char roleB1_;
  158. unsigned char roleB0_;
  159. unsigned char flags_;
  160. unsigned char reserved_[5];
  161. } impl;
  162. public:
  163. int role() const
  164. {
  165. return (impl.roleB1_ << 8 ) + impl.roleB0_;
  166. }
  167. void role(uint16_t r)
  168. {
  169. impl.roleB1_ = (unsigned char) ((r & 0xFF00) >> 8);
  170. impl.roleB0_ = (unsigned char) (r & 0x00FF);
  171. std::for_each(&impl.reserved_[0], &impl.reserved_[0] + 5, [](unsigned char &c)
  172. {
  173. c = 0;
  174. });
  175. }
  176. unsigned char flags() const
  177. {
  178. return impl.flags_;
  179. }
  180. void flags(unsigned char f)
  181. {
  182. impl.flags_ = f;
  183. std::for_each(&impl.reserved_[0], &impl.reserved_[0] + 5, [](unsigned char &c)
  184. {
  185. c = 0;
  186. });
  187. }
  188. };
  189. struct BeginRequestRecord
  190. {
  191. Header header_;
  192. BeginRequestBody body_;
  193. };
  194. class EndRequestBody
  195. {
  196. /// The underlying type of an EndRequestBody sub-header.
  197. /**
  198. * To guarantee the header is laid out exactly as we want, the
  199. * structure must be a POD-type (see http://tinyurl.com/yo9eav).
  200. */
  201. struct implementation_type
  202. {
  203. unsigned char appStatusB3_;
  204. unsigned char appStatusB2_;
  205. unsigned char appStatusB1_;
  206. unsigned char appStatusB0_;
  207. unsigned char protocolStatus_;
  208. unsigned char reserved_[3];
  209. } impl;
  210. public:
  211. typedef boost::asio::const_buffers_1 const_buffers_type;
  212. EndRequestBody() {}
  213. EndRequestBody( boost::uint64_t appStatus
  214. , status_types procStatus
  215. )
  216. {
  217. reset(appStatus, procStatus);
  218. }
  219. void reset( boost::uint64_t appStatus, status_types procStatus)
  220. {
  221. impl.appStatusB3_ = ( (appStatus >> 24) & 0xff );
  222. impl.appStatusB2_ = ( (appStatus >> 16) & 0xff );
  223. impl.appStatusB1_ = ( (appStatus >> 8) & 0xff );
  224. impl.appStatusB0_ = ( (appStatus >> 0) & 0xff );
  225. impl.protocolStatus_ = ((unsigned char)procStatus);
  226. memset(impl.reserved_, 0, sizeof(impl.reserved_));
  227. }
  228. const_buffers_type data() const
  229. {
  230. return boost::asio::buffer(
  231. static_cast<const void*>(&impl)
  232. , sizeof(impl));
  233. }
  234. };
  235. class EndRequestRecord
  236. {
  237. /// The underlying type of an EndRequestRecord sub-header.
  238. /**
  239. * To guarantee the header is laid out exactly as we want, the
  240. * structure must be a POD-type (see http://tinyurl.com/yo9eav).
  241. */
  242. struct implementation_type
  243. {
  244. Header header_;
  245. EndRequestBody body_;
  246. } impl;
  247. public:
  248. EndRequestRecord( boost::uint16_t id
  249. , boost::uint64_t appStatus
  250. , status_types procStatus
  251. )
  252. {
  253. impl.header_.reset( END_REQUEST, id, sizeof(EndRequestBody) );
  254. impl.body_.reset( appStatus, procStatus );
  255. }
  256. };
  257. class UnknownTypeBody
  258. {
  259. /// The underlying type of an UnknownTypeBody sub-header.
  260. /**
  261. * To guarantee the header is laid out exactly as we want, the
  262. * structure must be a POD-type (see http://tinyurl.com/yo9eav).
  263. */
  264. struct implementation_type
  265. {
  266. unsigned char type_;
  267. unsigned char reserved_[7];
  268. } impl;
  269. public:
  270. UnknownTypeBody()
  271. {
  272. }
  273. UnknownTypeBody( unsigned char t )
  274. {
  275. reset(t);
  276. }
  277. void reset(unsigned char t)
  278. {
  279. impl.type_ = t;
  280. memset(impl.reserved_, 0, sizeof(impl.reserved_));
  281. }
  282. unsigned char type() const { return impl.type_; }
  283. };
  284. class UnknownTypeRecord
  285. {
  286. /// The underlying type of a UnknownTypeRecord sub-header
  287. /**
  288. * To guarantee the header is laid out exactly as we want, the
  289. * structure must be a POD-type (see http://tinyurl.com/yo9eav).
  290. */
  291. struct implementation_type
  292. {
  293. Header header_;
  294. UnknownTypeBody body_;
  295. } impl;
  296. public:
  297. UnknownTypeRecord( int type )
  298. {
  299. impl.header_.reset( UNKNOWN_TYPE, 0, sizeof(UnknownTypeBody) );
  300. impl.body_.reset( (unsigned char)type ); // not sure why this is C-style
  301. }
  302. };
  303. #endif
  304. } // namespace detail
  305. namespace specification {
  306. /// Define the FastCGI spec using types.
  307. /**
  308. * Types are better than macros.
  309. */
  310. struct max_packet_size
  311. : boost::mpl::int_<65535u>
  312. {};
  313. struct header_length
  314. : boost::mpl::int_<8>
  315. {};
  316. struct listensock_fileno
  317. : boost::mpl::int_<0>
  318. {};
  319. static const unsigned char keep_connection = 1;
  320. struct null_request_id
  321. : boost::mpl::int_<0>
  322. {};
  323. template<typename Array>
  324. int get_version(Array& a) { return static_cast<int>(a[0]); }
  325. template<typename Array>
  326. spec_detail::request_types get_type(Array& a)
  327. {
  328. return static_cast<spec_detail::request_types>(a[1]);
  329. }
  330. template<typename Array>
  331. boost::uint16_t get_request_id(Array& a)
  332. {
  333. return (a[2] << 8) + a[3];
  334. }
  335. template<typename Array>
  336. boost::uint16_t get_content_length(Array& a)
  337. {
  338. return (a[4] << 8) + a[5];
  339. }
  340. template<typename Array>
  341. boost::uint16_t get_padding_length(Array& a)
  342. {
  343. return a[6];
  344. }
  345. template<typename Array>
  346. boost::uint16_t get_length(Array& a)
  347. {
  348. return get_content_length(a) + get_padding_length(a);
  349. }
  350. struct request_type
  351. {
  352. template<typename Array>
  353. static std::string to_string(Array& a)
  354. {
  355. switch(a[1])
  356. {
  357. case 0:
  358. return "ADMIN_REQUEST";
  359. case 1:
  360. return "BEGIN_REQUEST";
  361. case 2:
  362. return "ABORT_REQUEST";
  363. case 3:
  364. return "END_REQUEST";
  365. case 4:
  366. return "PARAMS";
  367. case 5:
  368. return "STDIN";
  369. case 6:
  370. return "STDOUT";
  371. case 7:
  372. return "STDERR";
  373. case 8:
  374. return "DATA";
  375. case 9:
  376. return "GET_VALUES";
  377. case 10:
  378. return "GET_VALUES_RESULT";
  379. case 11:
  380. return "UNKNOWN_TYPE";
  381. default:
  382. return "***ERROR-INVALID-TYPE***";
  383. }
  384. }
  385. };
  386. typedef spec_detail::Header header;
  387. typedef spec_detail::EndRequestBody end_request_body;
  388. struct begin_request
  389. : boost::mpl::int_<1>
  390. {
  391. struct body
  392. {
  393. typedef boost::mpl::int_<8> size;
  394. };
  395. typedef boost::array<
  396. unsigned char
  397. , header_length::value
  398. > buffer_type;
  399. buffer_type impl;
  400. begin_request(buffer_type& buf)
  401. : impl(buf)
  402. {
  403. }
  404. spec_detail::role_types role()
  405. {
  406. return static_cast<spec_detail::role_types>(
  407. (impl[0] << 8) + impl[1] );
  408. }
  409. unsigned char flags()
  410. {
  411. return impl[2];
  412. }
  413. template<typename Array>
  414. static spec_detail::role_types
  415. role(Array& a)
  416. {
  417. return static_cast<spec_detail::role_types>(
  418. (a[0] << 8) + a[1] );
  419. }
  420. template<typename Array>
  421. static unsigned char
  422. flags(Array& a)
  423. {
  424. return a[2];
  425. }
  426. };
  427. struct stdout_header
  428. : spec_detail::Header
  429. {
  430. explicit stdout_header()
  431. : Header(spec_detail::STDOUT,0,0)
  432. {
  433. }
  434. explicit stdout_header(int request_id, int content_len)
  435. : Header(spec_detail::STDOUT, request_id, content_len)
  436. {
  437. }
  438. void reset(int request_id, int content_len)
  439. {
  440. spec_detail::Header::reset(
  441. spec_detail::STDOUT
  442. , request_id
  443. , content_len);
  444. }
  445. };
  446. struct end_request
  447. : header
  448. , end_request_body
  449. {
  450. explicit end_request
  451. (
  452. int request_id = 0
  453. , boost::uint64_t app_status = 0
  454. , spec_detail::status_types proc_status
  455. = spec_detail::REQUEST_COMPLETE
  456. )
  457. : header(spec_detail::END_REQUEST, request_id
  458. , sizeof(end_request_body))
  459. , end_request_body(app_status, proc_status)
  460. {
  461. }
  462. void reset
  463. (
  464. int request_id
  465. , boost::uint64_t app_status = 0
  466. , spec_detail::status_types proc_status
  467. = spec_detail::REQUEST_COMPLETE
  468. )
  469. {
  470. header::reset(
  471. spec_detail::END_REQUEST
  472. , request_id
  473. , sizeof(end_request_body));
  474. end_request_body::reset(
  475. app_status, proc_status);
  476. }
  477. };
  478. class BeginRequestBody
  479. {
  480. /// The underlying type of a BeginRequestBody sub-header.
  481. /**
  482. * To guarantee the header is laid out exactly as we want, the
  483. * structure must be a POD-type (see http://tinyurl.com/yo9eav).
  484. */
  485. struct implementation_type
  486. {
  487. unsigned char roleB1_;
  488. unsigned char roleB0_;
  489. unsigned char flags_;
  490. unsigned char reserved_[5];
  491. } impl;
  492. public:
  493. int role() const
  494. {
  495. return (impl.roleB1_ << 8 ) + impl.roleB0_;
  496. }
  497. unsigned char flags() const
  498. {
  499. return impl.flags_;
  500. }
  501. };
  502. struct role_type
  503. {
  504. template<typename Array>
  505. static std::string to_string(Array& a)
  506. {
  507. using namespace spec_detail;
  508. switch(begin_request::role(a))
  509. {
  510. case RESPONDER:
  511. return "RESPONDER";
  512. case AUTHORIZER:
  513. return "AUTHORISER";
  514. case FILTER:
  515. return "FILTER";
  516. default:
  517. return "NONE";
  518. }
  519. }
  520. };
  521. //using namespace ::BOOST_CGI_NAMESPACE::fcgi::detail;
  522. }
  523. namespace spec = specification;
  524. } // namespace fcgi
  525. BOOST_CGI_NAMESPACE_END
  526. namespace boost {
  527. namespace fcgi {
  528. using namespace ::BOOST_CGI_NAMESPACE::fcgi;
  529. }
  530. }
  531. #endif // CGI_FCGI_SPECIFICATION_HPP_INCLUDED__