page.C 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /*
  2. This program is free software: you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation, either version 3 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. * */
  13. /*
  14. * page.C
  15. *
  16. * Created on: Jan 26, 2013
  17. * Author: xaxaxa
  18. */
  19. #include "include/page.H"
  20. #include "include/common.H"
  21. #include <stdexcept>
  22. #include <stdlib.h>
  23. #include <math.h>
  24. using namespace CP;
  25. namespace cppsp
  26. {
  27. static inline int itoa(int i, char* b) {
  28. static char const digit[] = "0123456789";
  29. char* p = b;
  30. //negative detection is not needed for this specific use-case
  31. //(writing the content-length header)
  32. /*if (i < 0) {
  33. *p++ = '-';
  34. i = -i;
  35. }*/
  36. p += (i==0?0:int(log10f(i))) + 1;
  37. *p = '\0';
  38. int l = p - b;
  39. do { //Move back, inserting digits as u go
  40. *--p = digit[i % 10];
  41. i = i / 10;
  42. } while (i);
  43. return l;
  44. }
  45. //inline-able memcpy() for copying SHORT STRINGS ONLY
  46. static inline void memcpy2_1(void* dst, const void* src, int len) {
  47. for (int i = 0; i < len; i++)
  48. ((char*) dst)[i] = ((const char*) src)[i];
  49. }
  50. Page::Page(Request& req, Response& resp, CP::StringPool* sp) :
  51. request(&req), response(&resp), sp(sp), doRender(true) {
  52. }
  53. void Page::__writeStringTable(int i, int len) {
  54. response->output.write(__stringTable + i, len);
  55. }
  56. void Page::handleRequest(Callback cb) {
  57. this->cb = cb;
  58. try {
  59. processRequest();
  60. } catch (exception& ex) {
  61. handleError(&ex, *response, this->filePath);
  62. flush();
  63. }
  64. }
  65. void Page::render(CP::StreamWriter& out) {
  66. out.write("This is the default page of the cppsp C++ "
  67. "web application framework. If you see this, it means "
  68. "you haven't overridden the render() method derived from cppsp::Page.");
  69. }
  70. void Page::doInit() {
  71. doReadPost = false;
  72. init();
  73. if (doReadPost && request->method.compare("POST") == 0) request->readPost( {
  74. &Page::_readPOSTCB, this });
  75. else initCB();
  76. }
  77. void Page::initCB() {
  78. try {
  79. load();
  80. //response->writeHeaders();
  81. if (doRender) render(response->output);
  82. flush();
  83. } catch (exception& ex) {
  84. handleError(&ex, *response, this->filePath);
  85. flush();
  86. }
  87. }
  88. void Page::cancelLoad(exception* ex) {
  89. if (ex == NULL) {
  90. runtime_error re("Web page execution cancelled");
  91. handleError(&re, *response, this->filePath);
  92. } else handleError(ex, *response, this->filePath);
  93. response->flush( { &Page::_flushCB, this });
  94. }
  95. void Page::_readPOSTCB(Request& r) {
  96. initCB();
  97. }
  98. void Page::flush() {
  99. if (response->closed) finalizeCB();
  100. else response->flush( { &Page::_flushCB, this });
  101. }
  102. String Page::mapPath(String path) {
  103. return server->mapPath(mapRelativePath(path), *sp);
  104. }
  105. String Page::mapPath(String path, RGC::Allocator& a) {
  106. return server->mapPath(mapRelativePath(path, a), a);
  107. }
  108. String Page::mapRelativePath(String path) {
  109. return mapRelativePath(path, *sp);
  110. }
  111. String Page::mapRelativePath(String path, RGC::Allocator& a) {
  112. char *tmp = (char*) a.alloc(request->path.length() + path.length());
  113. int l = combinePath(request->path.data(), request->path.length(), path.data(), path.length(),
  114. tmp);
  115. return {tmp,l};
  116. }
  117. void Page::loadNestedPage(String path, Delegate<void(Page*, exception* ex)> cb) {
  118. loadNestedPage(path, cb, *sp);
  119. }
  120. void Page::loadNestedPage(String path, Delegate<void(Page*, exception* ex)> cb,
  121. RGC::Allocator& a) {
  122. this->pageCB = cb;
  123. String tmp = mapRelativePath(path, a);
  124. server->loadPage(*poll, { tmp.data(), (int) tmp.length() }, a, { &Page::_pageCB, this });
  125. }
  126. void Page::loadNestedPageFromFile(String path, Delegate<void(Page*, exception* ex)> cb) {
  127. loadNestedPageFromFile(path, cb, *sp);
  128. }
  129. void Page::loadNestedPageFromFile(String path, Delegate<void(Page*, exception* ex)> cb,
  130. RGC::Allocator& a) {
  131. this->pageCB = cb;
  132. server->loadPageFromFile(*poll, { path.data(), (int) path.length() }, a, { &Page::_pageCB,
  133. this });
  134. }
  135. void Page::_pageCB(Page* p, exception* ex) {
  136. if (p != NULL) {
  137. p->request = request;
  138. p->response = response;
  139. p->poll = poll;
  140. p->server = server;
  141. }
  142. pageCB(p, ex);
  143. }
  144. void Page::_flushCB(Response& r) {
  145. flushCB();
  146. }
  147. void Page::finalizeCB() {
  148. if (this->cb != nullptr) cb();
  149. }
  150. Request::Request(CP::Stream& inp, CP::StringPool* sp) :
  151. inputStream(&inp), sp(sp), alloc(sp), headers(sp), queryString(less<String>(), alloc),
  152. form(less<String>(), alloc) {
  153. }
  154. void Request::parsePost(String buf) {
  155. struct
  156. {
  157. Request* This;
  158. void operator()(const char* name, int nameLen, const char* value, int valueLen) {
  159. String n, v;
  160. n = cppsp::urlDecode(name, nameLen, *This->sp);
  161. if (value != NULL) {
  162. v = cppsp::urlDecode(value, valueLen, *This->sp);
  163. } else v= {(char*)nullptr,0};
  164. This->form[n] = v;
  165. }
  166. } cb { this };
  167. cppsp::parseQueryString(buf.data(), buf.length(), &cb, false);
  168. }
  169. Response::Response(CP::Stream& out, CP::StringPool* sp) :
  170. outputStream(&out), output((CP::BufferedOutput&) buffer), sp(sp), alloc(sp),
  171. headers(less<String>(), alloc), headersWritten(false), closed(false),
  172. sendChunked(false) {
  173. addDefaultHeaders();
  174. }
  175. void Response::addDefaultHeaders() {
  176. statusCode = 200;
  177. statusName = "OK";
  178. headers.insert( { "Content-Type", "text/html; charset=UTF-8" });
  179. }
  180. void Response_doWriteHeaders(Response* This, CP::StreamWriter& sw) {
  181. //sw.writeF("HTTP/1.1 %i %s\r\n", This->statusCode, This->statusName);
  182. {
  183. char* s1 = sw.beginWrite(32);
  184. memcpy2_1(s1, "HTTP/1.1 ", 9);
  185. int x = 9 + itoa(This->statusCode, s1 + 9);
  186. s1[x] = ' ';
  187. x++;
  188. memcpy2_1(s1 + x, This->statusName.data(), This->statusName.length());
  189. x += This->statusName.length();
  190. s1[x] = '\r';
  191. s1[x + 1] = '\n';
  192. x += 2;
  193. sw.endWrite(x);
  194. }
  195. for (auto it = This->headers.begin(); it != This->headers.end(); it++) {
  196. int l1 = (*it).first.length();
  197. int l2 = (*it).second.length();
  198. char* tmp = sw.beginWrite(l1 + 4 + l2);
  199. memcpy2_1(tmp, (*it).first.data(), l1);
  200. tmp[l1] = ':';
  201. tmp[l1 + 1] = ' ';
  202. memcpy2_1(tmp + l1 + 2, (*it).second.data(), l2);
  203. tmp[l1 + 2 + l2] = '\r';
  204. tmp[l1 + 2 + l2 + 1] = '\n';
  205. sw.endWrite(l1 + 4 + l2);
  206. //sw.writeF("%s: %s\r\n", (*it).first.c_str(), (*it).second.c_str());
  207. }
  208. sw.write("\r\n", 2);
  209. }
  210. void Response::serializeHeaders(CP::StreamWriter& sw) {
  211. Response_doWriteHeaders(this, sw);
  212. }
  213. void Response::flush(Callback cb) {
  214. //printf("flush\n");
  215. if (closed) throw runtime_error("connection has already been closed by the client");
  216. output.flush();
  217. this->_cb = cb;
  218. if (likely(!headersWritten)) {
  219. headersWritten = true;
  220. int bufferL = buffer.length();
  221. {
  222. char* tmps = sp->beginAdd(16);
  223. int l = itoa(buffer.length(), tmps);
  224. sp->endAdd(l);
  225. this->headers.insert( { "Content-Length", { tmps, l } });
  226. CP::StreamWriter sw(buffer);
  227. Response_doWriteHeaders(this, sw);
  228. }
  229. iov[0]= {buffer.data()+bufferL, (size_t)(buffer.length()-bufferL)};
  230. iov[1]= {buffer.data(), (size_t)bufferL};
  231. outputStream->writevAll(iov, 2, { &Response::_writeCB, this });
  232. return;
  233. } else {
  234. if (buffer.length() <= 0) {
  235. cb(*this);
  236. return;
  237. }
  238. outputStream->write(buffer.data(), buffer.length(), { &Response::_writeCB, this });
  239. }
  240. }
  241. void Response::clear() {
  242. output.flush();
  243. buffer.clear();
  244. headersWritten = false;
  245. }
  246. void Response::_writeCB(int r) {
  247. if (r <= 0) closed = true;
  248. buffer.clear();
  249. _cb(*this);
  250. }
  251. void Request::reset() {
  252. headers.clear();
  253. queryString.clear();
  254. form.clear();
  255. }
  256. void Response::reset() {
  257. headers.clear();
  258. addDefaultHeaders();
  259. output.flush();
  260. buffer.clear();
  261. headersWritten = false;
  262. closed = false;
  263. sendChunked = false;
  264. }
  265. Server::Server() {
  266. handleRequest.attach( { &Server::defaultHandleRequest, this });
  267. }
  268. void Server::defaultHandleRequest(Request& req, Response& resp, Delegate<void()> cb) {
  269. if (req.path.length() > 6
  270. && memcmp(req.path.data() + (req.path.length() - 6), ".cppsp", 6) == 0) handleDynamicRequest(
  271. req.path, req, resp, cb);
  272. else handleStaticRequest(req.path, req, resp, cb);
  273. }
  274. String Server::mapPath(String path, RGC::Allocator& a) {
  275. String r = rootDir();
  276. char* tmp = (char*) a.alloc(path.length() + r.length());
  277. int l = cppsp::combinePathChroot(r.data(), r.length(), path.data(), path.length(), tmp);
  278. return String(tmp, l);
  279. }
  280. } /* namespace cppsp */
  281. string cppsp::Server::mapPath(string path) {
  282. String r = rootDir();
  283. char tmp[path.length() + r.length()];
  284. int l = cppsp::combinePathChroot(r.data(), r.length(), path.data(), path.length(), tmp);
  285. return string(tmp, l);
  286. }