Line data Source code
1 : // Snap Websites Server -- snap websites initialize website implementation
2 : // Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 :
18 :
19 : // self
20 : //
21 : #include "snapwebsites/snap_initialize_website.h"
22 :
23 :
24 : // snapwebsites lib
25 : //
26 : #include "snapwebsites/log.h"
27 : #include "snapwebsites/tcp_client_server.h"
28 : #include "snapwebsites/snapwebsites.h"
29 :
30 :
31 : // C++ lib
32 : //
33 : #include <sstream>
34 :
35 :
36 : // last include
37 : //
38 : #include <snapdev/poison.h>
39 :
40 :
41 :
42 :
43 :
44 :
45 : namespace snap
46 : {
47 :
48 :
49 0 : snap_initialize_website::snap_initialize_website_runner::snap_initialize_website_runner(snap_initialize_website * parent,
50 : QString const & snap_host, int snap_port, bool secure,
51 : QString const & website_uri, int destination_port,
52 0 : QString const & query_string, QString const & protocol)
53 : : snap_runner("initialize_website")
54 : , f_parent(parent)
55 : , f_snap_host(snap_host)
56 : , f_snap_port(snap_port)
57 : , f_secure(secure)
58 : , f_website_uri(website_uri)
59 : , f_destination_port(destination_port)
60 : , f_query_string(query_string)
61 0 : , f_protocol(protocol.toUpper())
62 : {
63 0 : if(f_protocol != "HTTP"
64 0 : && f_protocol != "HTTPS")
65 : {
66 0 : throw snap_initialize_website_exception_invalid_parameter("protocol must be \"HTTP\" or \"HTTPS\".");
67 : }
68 0 : }
69 :
70 :
71 0 : void snap_initialize_website::snap_initialize_website_runner::run()
72 : {
73 : try
74 : {
75 0 : send_init_command();
76 : }
77 0 : catch(...)
78 : {
79 : // we are in thread, throwing is not too good
80 : // TODO: add something to tell about the problem?
81 0 : message("Snap! Manager received an unknown exception while initializing a website.");
82 : }
83 0 : done();
84 0 : }
85 :
86 :
87 0 : void snap_initialize_website::snap_initialize_website_runner::send_init_command()
88 : {
89 0 : tcp_client_server::bio_client::pointer_t socket;
90 : try
91 : {
92 0 : tcp_client_server::bio_client::mode_t const mode(f_secure
93 0 : ? tcp_client_server::bio_client::mode_t::MODE_SECURE
94 : : tcp_client_server::bio_client::mode_t::MODE_PLAIN);
95 0 : socket.reset(new tcp_client_server::bio_client(f_snap_host.toUtf8().data(), f_snap_port, mode));
96 : }
97 0 : catch(tcp_client_server::tcp_client_server_runtime_error const&)
98 : {
99 0 : message("Snap! Manager was not able to connect to the Snap! Server (connection error).\n\nPlease verify that a Snap! server is running at the specified IP address.");
100 0 : return;
101 : }
102 :
103 : // send the #INIT command
104 : #define INIT_COMMAND "#INIT=" SNAPWEBSITES_VERSION_STRING
105 0 : if(socket->write(INIT_COMMAND "\n", sizeof(INIT_COMMAND)) != sizeof(INIT_COMMAND))
106 : {
107 0 : message("Snap! Manager was not able to communicate with the Snap! Server (write \"" INIT_COMMAND "\" error).");
108 0 : return;
109 : }
110 : #undef INIT_COMMAND
111 :
112 : // now send the environment
113 0 : std::stringstream ss;
114 :
115 : // HTTP_HOST
116 0 : ss << "HTTP_HOST=" << f_website_uri << std::endl;
117 :
118 : // HTTP_USER_AGENT
119 0 : ss << "HTTP_USER_AGENT=Snap/" << SNAPWEBSITES_VERSION_MAJOR
120 0 : << "." << SNAPWEBSITES_VERSION_MINOR
121 0 : << " (Linux) libsnapwebsites/" << SNAPWEBSITES_VERSION_MAJOR
122 0 : << "." << SNAPWEBSITES_VERSION_MINOR
123 0 : << "." << SNAPWEBSITES_VERSION_PATCH << std::endl;
124 :
125 : // HTTP_ACCEPT
126 0 : ss << "HTTP_ACCEPT=text/plain" << std::endl;
127 :
128 : // HTTP_ACCEPT_LANGUAGE
129 0 : ss << "HTTP_ACCEPT_LANGUAGE=en-us,en;q=0.8" << std::endl;
130 :
131 : // HTTP_ACCEPT_ENCODING
132 : //
133 : // TODO: add support for gzip compression
134 : //ss << "HTTP_ACCEPT_ENCODING=..." << std::endl;
135 :
136 : // HTTP_ACCEPT_CHARSET
137 0 : ss << "HTTP_ACCEPT_CHARSET=utf-8" << std::endl;
138 :
139 : // HTTP_CONNECTION
140 0 : ss << "HTTP_CONNECTION=close" << std::endl; // close once done
141 :
142 : // HTTP_CACHE_CONTROL
143 0 : ss << "HTTP_CACHE_CONTROL=max-age=0" << std::endl;
144 :
145 : // SERVER_SOFTWARE
146 0 : ss << "SERVER_SOFTWARE=Snap" << std::endl;
147 :
148 : // SERVER_NAME
149 : // TBD... requires a reverse DNS if f_snap_host is an IP
150 : //ss << "SERVER_NAME=" << f_snap_host << std::endl;
151 :
152 : // SERVER_ADDR
153 : // TODO: if f_snap_host is a name, convert to IP (use socket name?)
154 0 : ss << "SERVER_ADDR=" << f_snap_host << std::endl;
155 :
156 : // SERVER_PORT
157 0 : ss << "SERVER_PORT=" << f_destination_port << std::endl;
158 :
159 : // REMOTE_HOST
160 : char hostname[HOST_NAME_MAX + 1];
161 0 : hostname[HOST_NAME_MAX] = '\0';
162 0 : if(gethostname(hostname, HOST_NAME_MAX) == -1)
163 : {
164 0 : message("Snap! Manager could not determine your host name.");
165 0 : strncpy(hostname, "UNKNOWN", HOST_NAME_MAX);
166 : }
167 0 : ss << "REMOTE_HOST=" << hostname << std::endl;
168 :
169 : // REMOTE_ADDR
170 0 : ss << "REMOTE_ADDR=" << socket->get_client_addr() << std::endl;
171 :
172 : // REMOTE_PORT
173 0 : ss << "REMOTE_PORT=" << socket->get_client_port() << std::endl;
174 :
175 : // GATEWAY_INTERFACE
176 0 : ss << "GATEWAY_INTERFACE=libsnapwebsites/" << SNAPWEBSITES_VERSION_MAJOR
177 0 : << "." << SNAPWEBSITES_VERSION_MINOR
178 0 : << "." << SNAPWEBSITES_VERSION_PATCH << std::endl;
179 :
180 : // SERVER_PROTOCOL
181 0 : ss << "SERVER_PROTOCOL=HTTP/1.1" << std::endl;
182 :
183 : // REQUEST_METHOD
184 0 : ss << snap::get_name(name_t::SNAP_NAME_CORE_REQUEST_METHOD) << "=GET" << std::endl;
185 :
186 : // QUERY_STRING
187 0 : ss << "QUERY_STRING=initialize_website=1";
188 0 : if(!f_query_string.isEmpty())
189 : {
190 0 : ss << "&" << f_query_string;
191 : }
192 0 : ss << std::endl;
193 :
194 : // REQUEST_URI
195 0 : ss << snap::get_name(name_t::SNAP_NAME_CORE_REQUEST_URI) << "=/" << std::endl;
196 :
197 : // SCRIPT_NAME
198 : //ss << "SCRIPT_NAME=..." << std::endl;
199 :
200 : // HTTPS
201 : //
202 0 : if(f_protocol == "HTTPS")
203 : {
204 0 : ss << "HTTPS=on" << std::endl;
205 : }
206 :
207 : // send the environment in one block
208 0 : std::string const env(ss.str());
209 0 : std::cerr << "---ENV---\n" << env << "---ENV---\n";
210 0 : if(socket->write(env.c_str(), env.size()) != static_cast<int>(env.size()))
211 : {
212 0 : message("Snap! Manager was not able to communicate with the Snap! Server (write error while sending environment).");
213 0 : return;
214 : }
215 :
216 : // send the #END
217 0 : if(socket->write("#END\n", 5) != 5)
218 : {
219 0 : message("Snap! Manager was not able to communicate with the Snap! Server (write \"#END\" error).");
220 0 : return;
221 : }
222 :
223 : // read the results of the #START command
224 0 : bool started(false);
225 : for(;;)
226 : {
227 0 : std::string buf;
228 0 : int const r(socket->read_line(buf));
229 0 : if(r <= 0)
230 : {
231 : // note that r == 0 is not an error but it should not happen
232 : // (i.e. I/O is blocking so we should not return too soon.)
233 0 : if(started)
234 : {
235 0 : message("Snap! Manager never received the #END signal.");
236 : }
237 : else
238 : {
239 0 : message("Snap! Manager was not able to communicate with the Snap! Server (read error).");
240 : }
241 0 : return;
242 : }
243 0 : if(!started)
244 : {
245 0 : if(buf != "#START")
246 : {
247 0 : message("Snap! Manager was able to communicate with the Snap! Server but got unexpected protocol data.");
248 0 : return;
249 : }
250 0 : started = true;
251 : }
252 0 : else if(buf == "#END")
253 : {
254 : // got the #END mark, we are done
255 0 : break;
256 : }
257 : else
258 : {
259 0 : QString const line(QString::fromUtf8(buf.c_str(), buf.length()));
260 0 : message("Status: " + line);
261 : }
262 0 : }
263 : }
264 :
265 :
266 0 : void snap_initialize_website::snap_initialize_website_runner::message(QString const& msg)
267 : {
268 0 : snap_thread::snap_lock lock(f_mutex);
269 0 : f_message_queue.push_back(msg);
270 0 : }
271 :
272 :
273 0 : QString snap_initialize_website::snap_initialize_website_runner::next_message()
274 : {
275 : // TODO: It looks like we created our own queue here when the thread
276 : // implementation offers one that most certainly works just fine -- switch!
277 0 : snap_thread::snap_lock lock(f_mutex);
278 0 : if(f_message_queue.empty())
279 : {
280 0 : return QString();
281 : }
282 0 : QString const msg(f_message_queue.front());
283 0 : f_message_queue.pop_front();
284 0 : return msg;
285 : }
286 :
287 :
288 0 : bool snap_initialize_website::snap_initialize_website_runner::is_done() const
289 : {
290 0 : snap_thread::snap_lock lock(f_mutex);
291 0 : return f_done;
292 : }
293 :
294 :
295 0 : void snap_initialize_website::snap_initialize_website_runner::done()
296 : {
297 : // mark the process as done
298 0 : snap_thread::snap_lock lock(f_mutex);
299 0 : f_done = true;
300 0 : }
301 :
302 :
303 :
304 :
305 0 : snap_initialize_website::snap_initialize_website(QString const & snap_host, int snap_port, bool secure,
306 : QString const & website_uri, int destination_port,
307 0 : QString const & query_string, QString const & protocol)
308 0 : : f_website_runner(new snap_initialize_website_runner(this, snap_host, snap_port, secure, website_uri, destination_port, query_string, protocol))
309 0 : , f_process_thread(new snap_thread("Initialize Website Thread", f_website_runner.get()))
310 : {
311 0 : }
312 :
313 :
314 0 : bool snap_initialize_website::start_process()
315 : {
316 0 : if(!f_process_thread->start())
317 : {
318 0 : SNAP_LOG_FATAL("cannot start thread for website initialization!");
319 0 : return false;
320 : }
321 :
322 0 : return true;
323 : }
324 :
325 :
326 0 : QString snap_initialize_website::get_status()
327 : {
328 0 : return f_website_runner->next_message();
329 : }
330 :
331 :
332 0 : bool snap_initialize_website::is_done() const
333 : {
334 0 : return f_website_runner->is_done();
335 : }
336 :
337 :
338 :
339 6 : }
340 : // namespace snap
341 :
342 : // vim: ts=4 sw=4 et
|