1 #include <signal.h>
2 #include <cstdlib>
3 #include <cstring>
4 #include <string>
5 #include <vector>
6 #include <algorithm>
7 
8 #include <Wt/WServer.h>
9 #include <Wt/WResource.h>
10 #include <Wt/Http/Request.h>
11 #include <Wt/Http/Response.h>
12 #include <Wt/WTemplate.h>
13 #include <Wt/Utils.h>
14 
15 #include <Wt/Dbo/Dbo.h>
16 #include <Wt/Dbo/Json.h>
17 #ifndef BENCHMARK_USE_POSTGRES
18 #include <Wt/Dbo/backend/MySQL.h>
19 #else
20 #include <Wt/Dbo/backend/Postgres.h>
21 #endif
22 
23 #include <random>
24 
25 #ifndef WT_WIN32
26 extern char **environ;
27 #endif // WT_WIN32
28 
29 class MyMessage {
30 public:
31   std::string message;
32 
33   template<class Action>
persist(Action & a)34   void persist(Action& a) {
35     Wt::Dbo::field(a, message, "message");
36   }
37 };
38 
39 class World {
40 public:
41   int randomNumber;
42 
43   template<class Action>
persist(Action & a)44   void persist(Action& a) {
45     Wt::Dbo::field(a, randomNumber, "randomnumber");
46   }
47 };
48 
49 class Fortune {
50 public:
51   std::string message;
52 
53   template<class Action>
persist(Action & a)54   void persist(Action& a) {
55     Wt::Dbo::field(a, message, "message");
56   }
57 };
58 
59 namespace Wt {
60   namespace Dbo {
61     template<>
62     struct dbo_traits<World> : public dbo_default_traits {
versionFieldWt::Dbo::dbo_traits63       static const char *versionField() {
64         return 0;
65       }
invalidIdWt::Dbo::dbo_traits66       static IdType invalidId() {
67         return 0;
68       }
69     };
70     template<>
71     struct dbo_traits<Fortune> : public dbo_default_traits {
versionFieldWt::Dbo::dbo_traits72       static const char *versionField() {
73         return 0;
74       }
invalidIdWt::Dbo::dbo_traits75       static IdType invalidId() {
76         return 0;
77       }
78     };
79   }
80 }
81 
82 class JsonResource : public Wt::WResource {
83 public:
handleRequest(const Wt::Http::Request & request,Wt::Http::Response & response)84     virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
85         response.setMimeType("application/json");
86         response.addHeader("Server", "Wt");
87 
88         MyMessage message;
89         message.message = "Hello, World!";
90 
91         Wt::Dbo::JsonSerializer writer(response.out());
92         writer.serialize(message);
93     }
94 };
95 
96 class MyConnection : public
97 #ifdef BENCHMARK_USE_POSTGRES
98   Wt::Dbo::backend::Postgres
99 #else
100   Wt::Dbo::backend::MySQL
101 #endif
102 {
103 public:
104 #ifdef BENCHMARK_USE_POSTGRES
MyConnection(const std::string & db)105   MyConnection(const std::string &db) :
106   Wt::Dbo::backend::Postgres(db) {}
107 #else
108   MyConnection(const std::string &db, const std::string &dbuser, const std::string &dbpasswd, const std::string &dbhost, unsigned int dbport) :
109   Wt::Dbo::backend::MySQL(db, dbuser, dbpasswd, dbhost, dbport) {}
110 #endif
111 
startTransaction()112   virtual void startTransaction() { }
commitTransaction()113   virtual void commitTransaction() { }
rollbackTransaction()114   virtual void rollbackTransaction() { }
115 };
116 
117 struct DbStruct {
118   MyConnection *connection;
119   Wt::Dbo::Session session;
120 
121   std::default_random_engine rng;
122   std::uniform_int_distribution<int> distribution;
123 
DbStructDbStruct124   DbStruct()
125     : connection(0),
126       rng(clock()),
127       distribution(1, 10000) {
128     std::string dbHostStr = "localhost";
129     char *dbHost = std::getenv("DBHOST");
130     if (dbHost)
131       dbHostStr = std::string(dbHost);
132 #ifndef BENCHMARK_USE_POSTGRES
133     auto c = std::make_unique<MyConnection>("hello_world", "benchmarkdbuser", "benchmarkdbpass", dbHostStr, 3306);
134 #else
135     auto connStr = std::string("host=") + dbHostStr + " port=5432 user=benchmarkdbuser password=benchmarkdbpass dbname=hello_world";
136     auto c = std::make_unique<MyConnection>(connStr);
137 #endif
138 
139     connection = c.get();
140     session.setConnection(std::move(c));
141     session.mapClass<World>("world");
142     session.mapClass<Fortune>("fortune");
143   }
144 
randDbStruct145   int rand() {
146     return distribution(rng);
147   }
148 };
149 
150 namespace {
151   thread_local DbStruct *dbStruct_;
152 }
153 
154 class DbResource : public Wt::WResource {
155 public:
handleRequest(const Wt::Http::Request & request,Wt::Http::Response & response)156   virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
157     response.setMimeType("application/json");
158     response.addHeader("Server", "Wt");
159 
160     if (!dbStruct_) {
161       dbStruct_ = new DbStruct();
162     }
163 
164     Wt::Dbo::Transaction transaction(dbStruct_->session);
165     Wt::Dbo::ptr<World> entry = dbStruct_->session.load<World>(dbStruct_->rand());
166 
167     Wt::Dbo::JsonSerializer writer(response.out());
168     writer.serialize(entry);
169   }
170 };
171 
172 class QueriesResource : public Wt::WResource {
173 public:
handleRequest(const Wt::Http::Request & request,Wt::Http::Response & response)174   virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
175     int n;
176     if (const std::string *queries = request.getParameter("queries")) {
177       n = atoi(queries->c_str());
178       if (n < 1)
179         n = 1;
180       else if (n > 500)
181         n = 500;
182     } else {
183       n = 1;
184     }
185 
186     response.setMimeType("application/json");
187     response.addHeader("Server", "Wt");
188 
189     if (!dbStruct_) {
190       dbStruct_ = new DbStruct();
191     }
192 
193     Wt::Dbo::Transaction transaction(dbStruct_->session);
194     std::vector<Wt::Dbo::ptr<World> > results;
195     results.reserve(n);
196     for (int i = 0; i < n; ++i) {
197       results.push_back(dbStruct_->session.load<World>(dbStruct_->rand()));
198     }
199     Wt::Dbo::JsonSerializer writer(response.out());
200     writer.serialize(results);
201   }
202 };
203 
204 typedef Wt::Dbo::collection< Wt::Dbo::ptr<Fortune> > Fortunes;
205 typedef std::vector<Wt::Dbo::ptr<Fortune> > VFortunes;
206 
fortuneCmp(const Wt::Dbo::ptr<Fortune> & f1,const Wt::Dbo::ptr<Fortune> & f2)207 bool fortuneCmp(const Wt::Dbo::ptr<Fortune>& f1, const Wt::Dbo::ptr<Fortune>& f2) {
208   return strcmp(f1->message.c_str(), f2->message.c_str()) < 0;
209 }
210 
211 class FortuneTemplate : public Wt::WTemplate {
212 private:
213   const VFortunes *fortunes_;
214   mutable std::vector<Wt::Dbo::ptr<Fortune> >::const_iterator it_;
215 public:
FortuneTemplate(const std::vector<Wt::Dbo::ptr<Fortune>> & fortunes)216   FortuneTemplate(const std::vector<Wt::Dbo::ptr<Fortune> >& fortunes)
217     : Wt::WTemplate(tr("fortunes")),
218       fortunes_(&fortunes),
219       it_(fortunes.end())
220   {
221     addFunction("while", &Wt::WTemplate::Functions::while_f);
222   }
223 
conditionValue(const std::string & name) const224   virtual bool conditionValue(const std::string& name) const {
225     if (name == "next-fortune") {
226       if (it_ == fortunes_->end())
227         it_ = fortunes_->begin();
228       else
229         ++it_;
230 
231       if (it_ == fortunes_->end())
232         return false;
233 
234       return true;
235     } else
236       return Wt::WTemplate::conditionValue(name);
237   }
238 
resolveString(const std::string & varName,const std::vector<Wt::WString> & vars,std::ostream & result)239   virtual void resolveString(const std::string& varName, const std::vector<Wt::WString>& vars, std::ostream& result) {
240     if (varName == "id")
241       result << it_->id();
242     else if (varName == "message")
243       format(result, Wt::WString((*it_)->message));
244     else
245       Wt::WTemplate::resolveString(varName, vars, result);
246   }
247 };
248 
249 class FortuneResource : public Wt::WResource {
250 public:
handleRequest(const Wt::Http::Request & request,Wt::Http::Response & response)251   virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
252     response.setMimeType("text/html; charset=utf-8");
253     response.addHeader("Server", "Wt");
254 
255     if (!dbStruct_) {
256       dbStruct_ = new DbStruct();
257     }
258 
259     Wt::Dbo::Transaction transaction(dbStruct_->session);
260     Fortunes fortunes = dbStruct_->session.find<Fortune>();
261     VFortunes vFortunes;
262     for (Fortunes::const_iterator i = fortunes.begin(); i != fortunes.end(); ++i)
263       vFortunes.push_back(*i);
264     auto additionalFortune = std::make_unique<Fortune>();
265     additionalFortune->message = "Additional fortune added at request time.";
266     vFortunes.push_back(Wt::Dbo::ptr<Fortune>(std::move(additionalFortune)));
267 
268     std::sort(vFortunes.begin(), vFortunes.end(), fortuneCmp);
269 
270     FortuneTemplate tpl(vFortunes);
271 
272     response.out() << "<!DOCTYPE html>";
273     tpl.renderTemplate(response.out());
274   }
275 };
276 
277 class UpdateResource : public Wt::WResource {
278 public:
handleRequest(const Wt::Http::Request & request,Wt::Http::Response & response)279   virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
280     int n;
281     if (const std::string *queries = request.getParameter("queries")) {
282       n = atoi(queries->c_str());
283       if (n < 1)
284         n = 1;
285       else if (n > 500)
286         n = 500;
287     } else {
288       n = 1;
289     }
290 
291     response.setMimeType("application/json");
292     response.addHeader("Server", "Wt");
293 
294     if (!dbStruct_) {
295       dbStruct_ = new DbStruct();
296     }
297 
298     std::vector<Wt::Dbo::ptr<World> > results;
299 
300     for (int i = 0; i < n; ++i) {
301       bool success = false;
302       while (!success) {
303         try {
304           Wt::Dbo::Transaction transaction(dbStruct_->session);
305           Wt::Dbo::ptr<World> world = dbStruct_->session.load<World>(dbStruct_->rand());
306           world.modify()->randomNumber = dbStruct_->rand();
307           transaction.commit();
308           results.push_back(world);
309           success = true;
310         } catch (Wt::Dbo::Exception& e) {
311           // Retry
312         }
313       }
314     }
315 
316     Wt::Dbo::JsonSerializer writer(response.out());
317     writer.serialize(results);
318   }
319 };
320 
321 class PlaintextResource : public Wt::WResource {
handleRequest(const Wt::Http::Request & request,Wt::Http::Response & response)322   virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
323     response.setMimeType("text/plain");
324     response.addHeader("Server", "Wt");
325 
326     response.out() << "Hello, World!";
327   }
328 };
329 
main(int argc,char ** argv)330 int main(int argc, char** argv) {
331   try {
332     Wt::WServer server(argv[0]);
333 
334     server.setServerConfiguration(argc, argv, WTHTTP_CONFIGURATION);
335 
336     auto bundle = std::make_shared<Wt::WMessageResourceBundle>();
337     bundle->use(server.appRoot() + "fortunes");
338     server.setLocalizedStrings(bundle);
339 
340     JsonResource jsonResource;
341     server.addResource(&jsonResource, "/json");
342 
343     DbResource dbResource;
344     server.addResource(&dbResource, "/db");
345 
346     QueriesResource queriesResource;
347     server.addResource(&queriesResource, "/queries");
348 
349     FortuneResource fortuneResource;
350     server.addResource(&fortuneResource, "/fortune");
351 
352     UpdateResource updateResource;
353     server.addResource(&updateResource, "/updates");
354 
355     PlaintextResource plaintextResource;
356     server.addResource(&plaintextResource, "/plaintext");
357 
358     if (server.start()) {
359       int sig = Wt::WServer::waitForShutdown();
360 
361       std::cerr << "Shutdown (signal = " << sig << ")" << std::endl;
362       server.stop();
363 
364 #ifndef WT_WIN32
365       if (sig == SIGHUP)
366         Wt::WServer::restart(argc, argv, environ);
367 #endif // WT_WIN32
368     }
369   } catch (Wt::WServer::Exception& e) {
370     std::cerr << e.what() << "\n";
371     return 1;
372   } catch (std::exception& e) {
373     std::cerr << "exception: " << e.what() << "\n";
374     return 1;
375   }
376 }
377