Fixed a few problems, added a debugging configuration variable
[theodwalha.git] / source / client.cpp
blob4402dc4719953b3aefb4d52074242d5df070940d
1 #include <boost/bind.hpp>
2 #include <iostream>
3 #include <cstdlib>
4 #include <theodwalha/client.hpp>
5 #include <theodwalha/configuration.hpp>
6 #include <theodwalha/temporary.hpp>
7 #include <theodwalha/reply.hpp>
9 http_server_client::http_server_client(boost::asio::io_service & io_service, temporary_file_manager & file_manager, module_manager & modules):
10 socket(io_service),
11 file_manager(file_manager),
12 modules(modules),
13 keep_alive_counter(keep_alive_max)
15 read_buffer = new char[read_buffer_size];
18 http_server_client::~http_server_client()
20 delete read_buffer;
23 void http_server_client::start()
25 initialise();
26 read();
29 void http_server_client::initialise()
31 bytes_read = 0;
32 got_header = false;
35 void http_server_client::read()
37 if(debugging)
38 std::cout << "Reading" << std::endl;
40 socket.async_read_some(boost::asio::buffer(read_buffer, read_buffer_size), boost::bind(&http_server_client::read_event, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
43 void http_server_client::write(std::string const & data)
45 if(debugging)
46 std::cout << "Writing:" << std::endl << data << std::endl;
48 char * write_buffer = new char[data.size()];
49 std::memcpy(write_buffer, data.c_str(), data.size());
50 boost::asio::async_write(socket, boost::asio::buffer(write_buffer, data.size()), boost::bind(&http_server_client::write_event, this, boost::asio::placeholders::error, write_buffer));
53 void http_server_client::terminate()
55 if(debugging)
56 std::cout << "Terminating" << std::endl;
57 if(!temporary_file_name.empty())
59 temporary_file.close();
60 file_manager.release(temporary_file_name);
62 delete this;
65 void http_server_client::read_event(boost::system::error_code const & error, std::size_t bytes_in_buffer)
67 if(debugging)
68 std::cout << "Read event" << std::endl;
70 if(!error)
72 if(debugging)
73 std::cout << "Read " << bytes_in_buffer << " bytes" << std::endl;
74 bytes_read += bytes_in_buffer;
76 if(bytes_read > maximal_request_size)
78 std::cout << "Request too large" << std::endl;
79 terminate();
80 return;
83 std::string new_data(read_buffer, bytes_in_buffer);
85 if(temporary_file_name.empty())
87 extended_buffer += new_data;
89 if(debugging)
90 std::cout << extended_buffer << std::endl;
92 if(!got_header)
94 std::string error_message;
95 current_request = http_request();
96 process_header_result::type result = process_header(extended_buffer, current_request, error_message);
97 switch(result)
99 case process_header_result::no_delimiter:
100 std::cout << "No delimiter yet" << std::endl;
101 break;
103 case process_header_result::error:
104 std::cout << error_message << std::endl;
105 terminate();
106 return;
108 case process_header_result::success:
109 got_header = true;
110 if(debugging)
111 std::cout << "Retrieved the full HTTP header" << std::endl;
112 break;
116 if(extended_buffer.size() > maximal_extended_buffer_size)
118 if(!got_header)
120 std::cout << "A client exceeded the maximal extended buffer size without delivering the HTTP header" << std::endl;
121 terminate();
122 return;
125 temporary_file_name = file_manager.generate_name();
126 if(!temporary_file.open_create(temporary_file_name))
128 std::cout << "Failed to open temporary file " << temporary_file_name << std::endl;
129 exit(1);
132 temporary_file.write(extended_buffer);
133 extended_buffer.clear();
135 std::cout << "Using temporary file name " << temporary_file_name << std::endl;
138 else
139 temporary_file.write(new_data);
141 if(got_header)
143 std::size_t expected_byte_count = current_request.header_size + current_request.content_length;
144 if(debugging)
145 std::cout << "Expected byte count: " << current_request.header_size << " + " << current_request.content_length << " = " << expected_byte_count << std::endl;
146 if(bytes_read > expected_byte_count)
148 std::cout << "Received too many bytes from a client: " << bytes_read << " > " << expected_byte_count << std::endl;
149 terminate();
150 return;
152 else if(bytes_read == expected_byte_count)
154 if(debugging)
155 std::cout << "Ready to serve data for " << current_request.path << std::endl;
156 module_result result;
157 if(modules.process_request(current_request, result))
159 if(debugging)
160 std::cout << "The module manager successfully processed the request" << std::endl;
161 std::string data;
162 if(generate_content(current_request, result, data))
164 if(debugging)
165 std::cout << "Successfully generated the HTTP content, serving it to the client" << std::endl;
166 write(data);
167 return;
169 else
171 std::cout << "Failed to generate HTTP content" << std::endl;
172 terminate();
173 return;
176 else
178 std::cout << "The module manager failed to process this request" << std::endl;
179 terminate();
180 return;
185 read();
187 else
189 if(debugging)
190 std::cout << "Read error" << std::endl;
191 terminate();
195 void http_server_client::write_event(boost::system::error_code const & error, char * write_buffer)
197 delete write_buffer;
199 if(debugging)
200 std::cout << "Got a write event" << std::endl;
202 if(!error)
204 if(current_request.keep_alive)
206 keep_alive_counter--;
207 if(keep_alive_counter <= 0)
209 if(debugging)
210 std::cout << "Client keep alive counter is zero" << std::endl;
211 terminate();
213 else
215 if(debugging)
216 std::cout << "Reinitialising the state" << std::endl;
217 initialise();
218 read();
221 else
223 if(debugging)
224 std::cout << "No keep-alive" << std::endl;
225 terminate();
228 else
230 std::cout << "Write error" << std::endl;
231 terminate();
235 bool http_server_client::generate_content(http_request & request, module_result & result, std::string & content)
237 http_reply reply;
238 reply.protocol = request.protocol_version;
239 switch(result.return_code)
241 case request_handler_return_code::ok:
242 reply.ok();
243 break;
245 case request_handler_return_code::forbidden:
246 reply.forbidden();
247 break;
249 case request_handler_return_code::not_found:
250 reply.not_found();
251 break;
253 default:
254 std::cout << "Unknown request handler return code specified" << std::endl;
255 return false;
258 reply.gzip = request.gzip;
259 reply.keep_alive = request.keep_alive;
260 reply.content_type = result.content_type;
261 reply.keep_alive_timeout = keep_alive_timeout;
262 reply.keep_alive_max = keep_alive_counter;
263 reply.content = result.content;
265 if(!reply.get_packet(content))
267 std::cout << "Failed to build reply packet" << std::endl;
268 return false;
271 return true;