Skip to content

Commit 25d67ff

Browse files
committed
Progress in implementation of upload filter
1 parent f8ce4db commit 25d67ff

13 files changed

Lines changed: 254 additions & 29 deletions

cppcms/application.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,17 @@ namespace cppcms {
273273
///
274274
void assign_context(booster::shared_ptr<http::context> conn);
275275

276+
///
277+
/// Add context to applications such that context ownership isn't transferred
278+
/// to the application
279+
///
280+
void add_context(http::context &conn);
281+
282+
///
283+
/// Remove context added with add_context
284+
///
285+
void remove_context();
286+
276287
///
277288
/// Returns true if current application was created as asynchronous application.
278289
///

cppcms/applications_pool.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ namespace cppcms {
5151

5252
static const int thread_specific= 0x0010; ///< Make synchronous application thread specific
5353
static const int prepopulated = 0x0020; ///< Make sure all applications are created from the beginning (ignored in thread_specific is set)
54+
static const int content_filter = 0x0040; ///< Make this asynchronous application to handle content
5455
/// \cond INTERNAL
5556
static const int legacy = 0x8000; ///< Use legacy handling of application life time when the application is created in the event loop and than dispatched as a job to a thread pool
5657
/// \endcond

cppcms/http_context.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ namespace cppcms {
3333
namespace http {
3434
class request;
3535
class response;
36+
class file;
3637

3738
///
3839
/// \brief context is a central class that holds all specific connection related information.
@@ -190,6 +191,12 @@ namespace cppcms {
190191
///
191192
void submit_to_asynchronous_application(booster::intrusive_ptr<application> app,std::string const &matched_url);
192193
private:
194+
friend class impl::cgi::connection;
195+
bool has_file_filter();
196+
int send_to_file_filter(file &f,int stage);
197+
int on_headers_ready(bool has_content);
198+
int translate_exception();
199+
void make_error_message(std::exception const &e);
193200
void on_request_ready(bool error);
194201
void submit_to_pool_internal(booster::shared_ptr<application_specific_pool> pool,std::string const &matched,bool now);
195202
static void dispatch(booster::shared_ptr<application_specific_pool> const &pool,booster::shared_ptr<context> const &self,std::string const &url);

cppcms/http_request.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ namespace http {
2626
class cookie;
2727
class file;
2828
class content_limits;
29+
class content_filter;
30+
class basic_content_filter;
2931

3032
///
3133
/// \brief This class represents all information related to the HTTP/CGI request.
@@ -295,13 +297,29 @@ namespace http {
295297
/// Get content limits for incoming data processing
296298
///
297299
content_limits &limits();
300+
301+
///
302+
/// Get installed content filter
303+
///
304+
basic_content_filter *content_filter();
305+
306+
///
307+
/// Install content filter, setting it to 0 would remove the filter
308+
///
309+
void content_filter(basic_content_filter *flt);
310+
311+
///
312+
/// Returns true when full request content is ready
313+
///
314+
bool is_ready();
298315
public:
299316
/// \cond INTERNAL
300317
request(impl::cgi::connection &);
301318
~request();
302319
/// \endcond
303320
private:
304-
321+
void set_ready();
322+
friend class context;
305323
friend class impl::cgi::connection;
306324

307325
void set_post_data(std::vector<char> &post_data);

src/application.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ namespace cppcms {
3131

3232
struct application::_data {
3333
_data(cppcms::service *s):
34-
service(s)
34+
service(s),
35+
temp_conn(0)
3536
{
3637
}
3738
cppcms::service *service;
3839
booster::shared_ptr<http::context> conn;
40+
http::context *temp_conn;
3941
url_dispatcher url;
4042
booster::hold_ptr<url_mapper> url_map;
4143
std::vector<application *> managed_children;
@@ -92,10 +94,25 @@ booster::shared_ptr<http::context> application::get_context()
9294
return root()->d->conn;
9395
}
9496

97+
void application::add_context(http::context &conn)
98+
{
99+
if(root()->d->conn)
100+
throw cppcms_error("Context already assigned");
101+
root()->d->temp_conn = &conn;
102+
}
103+
104+
void application::remove_context()
105+
{
106+
root()->d->temp_conn = 0;
107+
}
108+
95109
http::context &application::context()
96110
{
97-
if(!root()->d->conn)
111+
if(!root()->d->conn) {
112+
if(root()->d->temp_conn)
113+
return *root()->d->temp_conn;
98114
throw cppcms_error("Access to unassigned context");
115+
}
99116
return *root()->d->conn;
100117
}
101118

@@ -118,6 +135,7 @@ bool application::is_asynchronous()
118135
void application::assign_context(booster::shared_ptr<http::context> conn)
119136
{
120137
root()->d->conn=conn;
138+
root()->d->temp_conn = 0;
121139
}
122140

123141
void application::set_pool(booster::weak_ptr<application_specific_pool> p)

src/cgi_api.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,12 @@ void connection::load_content(booster::system::error_code const &e,http::context
307307
return;
308308
}
309309

310+
int status = context->on_headers_ready(content_length > 0);
311+
if(status != 0) {
312+
handle_http_error(status,context,h);
313+
return;
314+
}
315+
310316
if(content_length > 0) {
311317
if(content_type.is_multipart_form_data()) {
312318
// 64 MB
@@ -364,8 +370,30 @@ void connection::on_some_multipart_read(booster::system::error_code const &e,siz
364370
char const *end = begin + n;
365371
multipart_parser::parsing_result_type r = multipart_parser::continue_input;
366372
long long allowed=context->request().limits().content_length_limit();
373+
bool has_filter = context->has_file_filter();
367374
while(begin!=end) {
368375
r = multipart_parser_->consume(begin,end);
376+
if(has_filter) {
377+
int status;
378+
switch(r) {
379+
case multipart_parser::meta_ready:
380+
status = context->send_to_file_filter(multipart_parser_->get_file(),0);
381+
break;
382+
case multipart_parser::content_partial:
383+
status = context->send_to_file_filter(multipart_parser_->get_file(),1);
384+
break;
385+
case multipart_parser::content_ready:
386+
status = context->send_to_file_filter(multipart_parser_->last_file(),2);
387+
break;
388+
default:
389+
status = 0;
390+
}
391+
if(status != 0) {
392+
handle_http_error(status,context,h);
393+
return;
394+
}
395+
}
396+
369397
if(r==multipart_parser::content_ready || r==multipart_parser::content_partial) {
370398
http::file &f= (r == multipart_parser::content_ready)
371399
? multipart_parser_->last_file()

src/http_context.cpp

Lines changed: 139 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <booster/log.h>
2222
#include <booster/backtrace.h>
2323
#include <booster/aio/io_service.h>
24+
#include <cppcms/http_content_filter.h>
2425

2526
#include "cached_settings.h"
2627

@@ -38,6 +39,9 @@ namespace http {
3839
std::auto_ptr<http::response> response;
3940
std::auto_ptr<cache_interface> cache;
4041
std::auto_ptr<session_interface> session;
42+
booster::shared_ptr<application_specific_pool> pool;
43+
booster::intrusive_ptr<application> app;
44+
std::string matched;
4145
_data(context &cntx) :
4246
locale(cntx.connection().service().locale()),
4347
request(cntx.connection())
@@ -120,6 +124,20 @@ namespace {
120124
}
121125

122126
};
127+
128+
class context_guard {
129+
public:
130+
context_guard(cppcms::application &app,cppcms::http::context &ctx) : app_(&app)
131+
{
132+
app_->add_context(ctx);
133+
}
134+
~context_guard()
135+
{
136+
app_->remove_context();
137+
}
138+
private:
139+
cppcms::application *app_;
140+
};
123141
}
124142

125143

@@ -167,10 +185,27 @@ void context::submit_to_pool_internal(booster::shared_ptr<application_specific_p
167185
}
168186
}
169187

170-
void context::on_request_ready(bool error)
188+
int context::translate_exception()
171189
{
172-
if(error) return;
190+
try {
191+
throw;
192+
}
193+
catch(abort_upload const &e) {
194+
return e.code();
195+
}
196+
catch(std::exception const &e) {
197+
make_error_message(e);
198+
return 500;
199+
}
200+
catch(...) {
201+
BOOSTER_ERROR("cppcms") << "Unknown exception";
202+
return 500;
203+
}
204+
return 0;
205+
}
173206

207+
int context::on_headers_ready(bool has_content)
208+
{
174209
char const *host = conn_->cgetenv("HTTP_HOST");
175210
char const *path_info = conn_->cgetenv("PATH_INFO");
176211
char const *script_name = conn_->cgetenv("SCRIPT_NAME");
@@ -183,15 +218,94 @@ void context::on_request_ready(bool error)
183218
path_info,
184219
matched
185220
);
221+
if(!pool)
222+
return 404;
223+
224+
int flags;
225+
if(!has_content || ((flags=pool->flags()) & app::op_mode_mask) == app::synchronous || (flags & app::content_filter)==0) {
226+
d->pool.swap(pool);
227+
d->matched.swap(matched);
228+
return 0;
229+
}
230+
231+
booster::intrusive_ptr<application> app = d->pool->get(service());
232+
if(!app)
233+
return 500;
234+
235+
try {
236+
context_guard g(*app,*this);
237+
app->main(matched);
238+
}
239+
catch(...) {
240+
return translate_exception();
241+
}
242+
d->pool.swap(pool);
243+
d->app.swap(app);
244+
d->matched.swap(matched);
245+
return 0;
246+
}
247+
248+
bool context::has_file_filter()
249+
{
250+
return dynamic_cast<multipart_filter *>(request().content_filter())!=0;
251+
}
252+
int context::send_to_file_filter(file &f,int stage)
253+
{
254+
try {
255+
context_guard g(*d->app,*this);
256+
multipart_filter *filter=static_cast<multipart_filter *>(request().content_filter());
257+
switch(stage) {
258+
case 0: filter->on_new_file(f); break;
259+
case 1: filter->on_upload_progress(f); break;
260+
case 2: filter->on_data_ready(f); break;
261+
}
262+
}
263+
catch(...) {
264+
return translate_exception();
265+
}
266+
return 0;
267+
}
186268

187-
if(!pool) {
188-
response().io_mode(http::response::asynchronous);
189-
response().make_error_response(http::response::not_found);
190-
async_complete_response();
269+
void context::on_request_ready(bool error)
270+
{
271+
booster::shared_ptr<application_specific_pool> pool;
272+
booster::intrusive_ptr<application> app;
273+
pool.swap(d->pool);
274+
app.swap(d->app);
275+
basic_content_filter *filter = 0;
276+
277+
if(error && app && (filter=request().content_filter())!=0) {
278+
context_guard g(*app,*this);
279+
try {
280+
filter->on_error();
281+
}
282+
catch(...) {}
191283
return;
192284
}
193285

194-
submit_to_pool_internal(pool,matched,true);
286+
if(error)
287+
return;
288+
289+
request().set_ready();
290+
291+
if(app && filter) {
292+
context_guard g(*app,*this);
293+
try {
294+
filter->on_end_of_content();
295+
}
296+
catch(...) {
297+
translate_exception();
298+
return;
299+
}
300+
}
301+
302+
if(app) {
303+
app->assign_context(self());
304+
dispatch(app,d->matched,false);
305+
return;
306+
}
307+
308+
submit_to_pool_internal(pool,d->matched,true);
195309
}
196310

197311
namespace {
@@ -226,6 +340,21 @@ void context::dispatch(booster::shared_ptr<application_specific_pool> const &poo
226340
app->assign_context(self);
227341
dispatch(app,url,true);
228342
}
343+
344+
void context::make_error_message(std::exception const &e)
345+
{
346+
BOOSTER_ERROR("cppcms") << "Caught exception ["<<e.what()<<"]\n" << booster::trace(e) ;
347+
if(!response().some_output_was_written()) {
348+
if(service().cached_settings().security.display_error_message) {
349+
std::ostringstream ss;
350+
ss << e.what() << '\n';
351+
ss << booster::trace(e);
352+
response().make_error_response(http::response::internal_server_error,ss.str());
353+
}
354+
else
355+
response().make_error_response(http::response::internal_server_error);
356+
}
357+
}
229358
// static
230359
void context::dispatch(booster::intrusive_ptr<application> const &app,std::string const &url,bool syncronous)
231360
{
@@ -248,20 +377,9 @@ void context::dispatch(booster::intrusive_ptr<application> const &app,std::strin
248377
app->response().make_error_response(http::response::forbidden);
249378
}
250379
}
251-
catch(std::exception const &e){
252-
BOOSTER_ERROR("cppcms") << "Caught exception ["<<e.what()<<"]\n" << booster::trace(e) ;
253-
if(app->get_context()) {
254-
if(!app->response().some_output_was_written()) {
255-
if(app->service().cached_settings().security.display_error_message) {
256-
std::ostringstream ss;
257-
ss << e.what() << '\n';
258-
ss << booster::trace(e);
259-
app->response().make_error_response(http::response::internal_server_error,ss.str());
260-
}
261-
else
262-
app->response().make_error_response(http::response::internal_server_error);
263-
}
264-
}
380+
catch(...){
381+
if(app->get_context())
382+
app->context().translate_exception();
265383
}
266384

267385
if(app->get_context()) {

0 commit comments

Comments
 (0)