- easier filter integration - putting right responsibilitymaster
@@ -55,8 +55,8 @@ namespace http { | |||
content_limits(); | |||
~content_limits(); | |||
size_t content_length_limit() const; | |||
void content_length_limit(size_t size); | |||
long long content_length_limit() const; | |||
void content_length_limit(long long size); | |||
long long multipart_form_data_limit() const; | |||
void multipart_form_data_limit(long long size); | |||
@@ -69,7 +69,7 @@ namespace http { | |||
private: | |||
size_t content_length_limit_; | |||
long long content_length_limit_; | |||
size_t file_in_memory_limit_; | |||
long long multipart_form_data_limit_; | |||
std::string uploads_path_; | |||
@@ -92,7 +92,7 @@ namespace http { | |||
booster::hold_ptr<_data> d; | |||
}; | |||
class CPPCMS_API raw_content_filter : basic_content_filter { | |||
class CPPCMS_API raw_content_filter : public basic_content_filter { | |||
public: | |||
virtual void on_data_chunk(void const *data,size_t data_size) = 0; | |||
virtual ~raw_content_filter(); | |||
@@ -192,9 +192,8 @@ namespace cppcms { | |||
void submit_to_asynchronous_application(booster::intrusive_ptr<application> app,std::string const &matched_url); | |||
private: | |||
friend class impl::cgi::connection; | |||
bool has_file_filter(); | |||
int send_to_file_filter(file &f,int stage); | |||
int on_headers_ready(bool has_content); | |||
int on_content_progress(size_t n); | |||
int on_headers_ready(); | |||
int translate_exception(); | |||
void make_error_message(std::exception const &e); | |||
void on_request_ready(bool error); | |||
@@ -318,14 +318,18 @@ namespace http { | |||
~request(); | |||
/// \endcond | |||
private: | |||
void set_ready(); | |||
friend class context; | |||
friend class impl::cgi::connection; | |||
void set_post_data(std::vector<char> &post_data); | |||
void set_post_data(std::vector<booster::shared_ptr<file> > const &multipart); | |||
bool prepare(); | |||
int on_content_start(); | |||
void on_error(); | |||
int on_content_progress(size_t n); | |||
std::pair<char *,size_t > get_buffer(); | |||
bool size_ok(file &f,long long size); | |||
std::pair<char *,size_t> get_content_buffer(); | |||
bool prepare(); | |||
bool parse_cookies(); | |||
std::string urlencoded_decode(char const *,char const *); | |||
bool parse_form_urlencoded(char const *begin,char const *end,form_type &out); | |||
@@ -162,20 +162,16 @@ namespace cgi { | |||
void set_error(ehandler const &h,std::string s); | |||
void on_headers_read(booster::system::error_code const &e,http::context *,ehandler const &h); | |||
void load_content(booster::system::error_code const &e,http::context *,ehandler const &h); | |||
void on_post_data_loaded(booster::system::error_code const &e,size_t ,http::context *,ehandler const &h); | |||
void on_some_multipart_read(booster::system::error_code const &e,size_t n,http::context *,ehandler const &h); | |||
void load_content(http::context *,ehandler const &h); | |||
void on_some_content_read(booster::system::error_code const &e,size_t n,http::context *,ehandler const &h); | |||
void handle_eof(callback const &on_eof); | |||
void handle_http_error(int code,http::context *context,ehandler const &h); | |||
void handle_http_error_eof(booster::system::error_code const &e,int code,ehandler const &h); | |||
std::vector<char> content_; | |||
cppcms::service *service_; | |||
std::string async_chunk_; | |||
std::string error_; | |||
bool request_in_progress_; | |||
long long read_size_; | |||
std::auto_ptr<multipart_parser> multipart_parser_; | |||
std::map<std::string,std::string> map_env_; | |||
@@ -65,7 +65,7 @@ namespace cppcms { | |||
~multipart_parser() | |||
{ | |||
} | |||
typedef enum { | |||
parsing_error, | |||
meta_ready, | |||
@@ -75,6 +75,17 @@ namespace cppcms { | |||
eof, | |||
no_room_left | |||
} parsing_result_type; | |||
static bool is_ok(parsing_result_type r) { | |||
switch(r) { | |||
case meta_ready: | |||
case content_partial: | |||
case content_ready: | |||
case continue_input: | |||
return true; | |||
default: | |||
return false; | |||
} | |||
} | |||
bool has_file() { return file_is_ready_; } | |||
http::file &get_file() | |||
@@ -132,8 +143,10 @@ namespace cppcms { | |||
case expecting_eof_lf: | |||
if(*buffer!='\n') | |||
return parsing_error; | |||
if(buffer + 1 == buffer_end) | |||
if(buffer + 1 == buffer_end) { | |||
buffer++; | |||
return eof; | |||
} | |||
else | |||
return parsing_error; | |||
case expecting_lf: | |||
@@ -18,7 +18,6 @@ | |||
#include "cached_settings.h" | |||
#include <cppcms/json.h> | |||
#include "cgi_api.h" | |||
#include "multipart_parser.h" | |||
#include <cppcms/util.h> | |||
#include <scgi_header.h> | |||
#include <stdlib.h> | |||
@@ -199,8 +198,7 @@ void connection::on_headers_read(booster::system::error_code const &e,http::cont | |||
h(http::context::operation_aborted); | |||
return; | |||
} | |||
context->request().prepare(); | |||
load_content(e,context,h); | |||
load_content(context,h); | |||
} | |||
void connection::aync_wait_for_close_by_peer(booster::callback<void()> const &on_eof) | |||
@@ -224,39 +222,41 @@ void connection::set_error(ehandler const &h,std::string s) | |||
void connection::handle_http_error(int code,http::context *context,ehandler const &h) | |||
{ | |||
async_chunk_.clear(); | |||
async_chunk_.reserve(256); | |||
std::string status; | |||
status.reserve(128); | |||
status += char('0' + code/100); | |||
status += char('0' + code/10 % 10); | |||
status += char('0' + code % 10); | |||
status += ' '; | |||
status += http::response::status_to_string(code); | |||
if(context->service().cached_settings().service.generate_http_headers) { | |||
async_chunk_ += "HTTP/1.0 "; | |||
async_chunk_ += status; | |||
async_chunk_ += "\r\n" | |||
"Connection: close\r\n" | |||
"Content-Type: text/html\r\n" | |||
"\r\n"; | |||
} | |||
else { | |||
async_chunk_ += "Content-Type: text/html\r\n" | |||
"Status: "; | |||
async_chunk_ += status; | |||
async_chunk_ += "\r\n" | |||
"\r\n"; | |||
} | |||
if(!context->response().some_output_was_written()) { | |||
async_chunk_.reserve(256); | |||
std::string status; | |||
status.reserve(128); | |||
status += char('0' + code/100); | |||
status += char('0' + code/10 % 10); | |||
status += char('0' + code % 10); | |||
status += ' '; | |||
status += http::response::status_to_string(code); | |||
if(context->service().cached_settings().service.generate_http_headers) { | |||
async_chunk_ += "HTTP/1.0 "; | |||
async_chunk_ += status; | |||
async_chunk_ += "\r\n" | |||
"Connection: close\r\n" | |||
"Content-Type: text/html\r\n" | |||
"\r\n"; | |||
} | |||
else { | |||
async_chunk_ += "Content-Type: text/html\r\n" | |||
"Status: "; | |||
async_chunk_ += status; | |||
async_chunk_ += "\r\n" | |||
"\r\n"; | |||
} | |||
async_chunk_ += | |||
"<html>\r\n" | |||
"<body>\r\n" | |||
"<h1>"; | |||
async_chunk_ += status; | |||
async_chunk_ += "</h1>\r\n" | |||
"</body>\r\n" | |||
"</html>\r\n"; | |||
async_chunk_ += | |||
"<html>\r\n" | |||
"<body>\r\n" | |||
"<h1>"; | |||
async_chunk_ += status; | |||
async_chunk_ += "</h1>\r\n" | |||
"</body>\r\n" | |||
"</html>\r\n"; | |||
} | |||
async_write(booster::aio::buffer(async_chunk_),true, | |||
mfunc_to_event_handler( | |||
&connection::handle_http_error_eof, | |||
@@ -280,172 +280,56 @@ void connection::handle_http_error_eof( | |||
void connection::load_content(booster::system::error_code const &e,http::context *context,ehandler const &h) | |||
void connection::load_content(http::context *context,ehandler const &h) | |||
{ | |||
if(e) { | |||
set_error(h,e.message()); | |||
return; | |||
} | |||
http::content_type content_type = context->request().content_type_parsed(); | |||
char const *s_content_length=cgetenv("CONTENT_LENGTH"); | |||
long long content_length = *s_content_length == 0 ? 0 : atoll(s_content_length); | |||
if(content_length < 0) { | |||
handle_http_error(400,context,h); | |||
return; | |||
} | |||
int status = context->on_headers_ready(content_length > 0); | |||
if(status != 0) { | |||
int status=0; | |||
if((status = context->on_headers_ready())!=0) { | |||
handle_http_error(status,context,h); | |||
return; | |||
} | |||
if(content_length > 0) { | |||
if(content_type.is_multipart_form_data()) { | |||
// 64 MB | |||
long long allowed=context->request().limits().multipart_form_data_limit(); | |||
if(content_length > allowed) { | |||
BOOSTER_NOTICE("cppcms") << "multipart/form-data size too big " << content_length << | |||
" REMOTE_ADDR = `" << getenv("REMOTE_ADDR") << "' REMOTE_HOST=`" << getenv("REMOTE_HOST") << "'"; | |||
handle_http_error(413,context,h); | |||
return; | |||
} | |||
multipart_parser_.reset(new multipart_parser( | |||
context->request().limits().uploads_path(), | |||
context->request().limits().file_in_memory_limit())); | |||
read_size_ = content_length; | |||
if(!multipart_parser_->set_content_type(content_type)) { | |||
BOOSTER_NOTICE("cppcms") << "Invalid multipart/form-data request" << content_length << | |||
" REMOTE_ADDR = `" << getenv("REMOTE_ADDR") << "' REMOTE_HOST=`" << getenv("REMOTE_HOST") << "'"; | |||
handle_http_error(400,context,h); | |||
return; | |||
} | |||
content_.clear(); | |||
content_.resize(8192); | |||
async_read_some(&content_.front(),content_.size(), | |||
mfunc_to_io_handler(&connection::on_some_multipart_read, | |||
self(), | |||
context, | |||
h)); | |||
} | |||
else { | |||
long long allowed=context->request().limits().content_length_limit(); | |||
if(content_length > allowed) { | |||
BOOSTER_NOTICE("cppcms") << "POST data size too big " << content_length << | |||
" REMOTE_ADDR = `" << getenv("REMOTE_ADDR") << "' REMOTE_HOST=`" << getenv("REMOTE_HOST") << "'"; | |||
handle_http_error(413,context,h); | |||
return; | |||
} | |||
content_.clear(); | |||
content_.resize(content_length,0); | |||
async_read( &content_.front(), | |||
content_.size(), | |||
mfunc_to_io_handler(&connection::on_post_data_loaded,self(),context,h)); | |||
} | |||
if(context->request().content_length() > 0) { | |||
std::pair<char *,size_t> buffer = context->request().get_buffer(); | |||
async_read_some(buffer.first,buffer.second, | |||
mfunc_to_io_handler(&connection::on_some_content_read, | |||
self(), | |||
context, | |||
h)); | |||
} | |||
else { | |||
on_post_data_loaded(booster::system::error_code(),0,context,h); | |||
on_async_read_complete(); | |||
h(http::context::operation_completed); | |||
} | |||
} | |||
void connection::on_some_multipart_read(booster::system::error_code const &e,size_t n,http::context *context,ehandler const &h) | |||
void connection::on_some_content_read(booster::system::error_code const &e,size_t n,http::context *context,ehandler const &h) | |||
{ | |||
if(e) { set_error(h,e.message()); return; } | |||
read_size_-=n; | |||
if(read_size_ < 0) { handle_http_error(400,context,h); return ;} | |||
char const *begin = &content_.front(); | |||
char const *end = begin + n; | |||
multipart_parser::parsing_result_type r = multipart_parser::continue_input; | |||
long long allowed=context->request().limits().content_length_limit(); | |||
bool has_filter = context->has_file_filter(); | |||
while(begin!=end) { | |||
r = multipart_parser_->consume(begin,end); | |||
if(has_filter) { | |||
int status; | |||
switch(r) { | |||
case multipart_parser::meta_ready: | |||
status = context->send_to_file_filter(multipart_parser_->get_file(),0); | |||
break; | |||
case multipart_parser::content_partial: | |||
status = context->send_to_file_filter(multipart_parser_->get_file(),1); | |||
break; | |||
case multipart_parser::content_ready: | |||
status = context->send_to_file_filter(multipart_parser_->last_file(),2); | |||
break; | |||
default: | |||
status = 0; | |||
} | |||
if(status != 0) { | |||
handle_http_error(status,context,h); | |||
return; | |||
} | |||
} | |||
if(r==multipart_parser::content_ready || r==multipart_parser::content_partial) { | |||
http::file &f= (r == multipart_parser::content_ready) | |||
? multipart_parser_->last_file() | |||
: multipart_parser_->get_file(); | |||
if(!f.has_mime() && f.size() > allowed) { | |||
BOOSTER_NOTICE("cppcms") << "multipart/form-data non-file entry size too big " << | |||
f.size() | |||
<< " REMOTE_ADDR = `" << getenv("REMOTE_ADDR") | |||
<< "' REMOTE_HOST=`" << getenv("REMOTE_HOST") << "'"; | |||
handle_http_error(413,context,h); | |||
return; | |||
} | |||
continue; | |||
} | |||
else if(r==multipart_parser::meta_ready) | |||
continue; | |||
break; | |||
int status = context->on_content_progress(n); | |||
if(status !=0) { | |||
handle_http_error(status,context,h); | |||
return; | |||
} | |||
if(r == multipart_parser::eof) { | |||
if(read_size_ != 0) { | |||
handle_http_error(400,context,h); | |||
return; | |||
} | |||
content_.clear(); | |||
multipart_parser::files_type files = multipart_parser_->get_files(); | |||
context->request().set_post_data(files); | |||
multipart_parser_.reset(); | |||
std::pair<char *,size_t> buffer = context->request().get_buffer(); | |||
if(buffer.second==0) { | |||
on_async_read_complete(); | |||
h(http::context::operation_completed); | |||
return; | |||
} | |||
else if (r==multipart_parser::parsing_error) { | |||
handle_http_error(400,context,h); | |||
return; | |||
} | |||
else if(r==multipart_parser::no_room_left) { | |||
handle_http_error(413,context,h); | |||
return; | |||
} | |||
else if(read_size_ == 0) { | |||
handle_http_error(400,context,h); | |||
return; | |||
} | |||
else { | |||
async_read_some(&content_.front(),content_.size(), | |||
mfunc_to_io_handler(&connection::on_some_multipart_read, | |||
async_read_some(buffer.first,buffer.second, | |||
mfunc_to_io_handler(&connection::on_some_content_read, | |||
self(), | |||
context, | |||
h)); | |||
} | |||
} | |||
void connection::on_post_data_loaded(booster::system::error_code const &e,size_t /*unused*/,http::context *context,ehandler const &h) | |||
{ | |||
if(e) { set_error(h,e.message()); return; } | |||
context->request().set_post_data(content_); | |||
on_async_read_complete(); | |||
h(http::context::operation_completed); | |||
} | |||
bool connection::is_reuseable() | |||
{ | |||
return error_.empty() && keep_alive(); | |||
@@ -45,7 +45,7 @@ std::string abort_upload::message() const | |||
struct content_limits::_data {}; | |||
content_limits::content_limits(impl::cached_settings const &s) : | |||
content_length_limit_(s.security.content_length_limit * 1024), | |||
content_length_limit_(s.security.content_length_limit * 1024LL), | |||
file_in_memory_limit_(s.security.file_in_memory_limit), | |||
multipart_form_data_limit_(s.security.multipart_form_data_limit * 1024LL), | |||
uploads_path_(s.security.uploads_path) | |||
@@ -63,8 +63,8 @@ content_limits::content_limits() : | |||
{ | |||
} | |||
size_t content_limits::content_length_limit() const { return content_length_limit_; } | |||
void content_limits::content_length_limit(size_t size) { content_length_limit_=size; } | |||
long long content_limits::content_length_limit() const { return content_length_limit_; } | |||
void content_limits::content_length_limit(long long size) { content_length_limit_=size; } | |||
long long content_limits::multipart_form_data_limit() const { return multipart_form_data_limit_; } | |||
void content_limits::multipart_form_data_limit(long long size) { multipart_form_data_limit_=size; } | |||
@@ -129,11 +129,13 @@ namespace { | |||
public: | |||
context_guard(cppcms::application &app,cppcms::http::context &ctx) : app_(&app) | |||
{ | |||
app_->add_context(ctx); | |||
if(app_) | |||
app_->add_context(ctx); | |||
} | |||
~context_guard() | |||
{ | |||
app_->remove_context(); | |||
if(app_) | |||
app_->remove_context(); | |||
} | |||
private: | |||
cppcms::application *app_; | |||
@@ -204,13 +206,14 @@ int context::translate_exception() | |||
return 0; | |||
} | |||
int context::on_headers_ready(bool has_content) | |||
int context::on_headers_ready() | |||
{ | |||
char const *host = conn_->cgetenv("HTTP_HOST"); | |||
char const *path_info = conn_->cgetenv("PATH_INFO"); | |||
char const *script_name = conn_->cgetenv("SCRIPT_NAME"); | |||
std::string matched; | |||
booster::intrusive_ptr<application> app; | |||
booster::shared_ptr<application_specific_pool> pool = | |||
service().applications_pool().get_application_specific_pool( | |||
host, | |||
@@ -220,50 +223,37 @@ int context::on_headers_ready(bool has_content) | |||
); | |||
if(!pool) | |||
return 404; | |||
request().prepare(); | |||
int flags; | |||
if(!has_content || ((flags=pool->flags()) & app::op_mode_mask) == app::synchronous || (flags & app::content_filter)==0) { | |||
d->pool.swap(pool); | |||
d->matched.swap(matched); | |||
return 0; | |||
if(request().content_length() != 0 && ((flags=pool->flags()) & app::op_mode_mask) != app::synchronous && (flags & app::content_filter)!=0) { | |||
app = pool->get(service()); | |||
if(!app) | |||
return 500; | |||
try { | |||
context_guard g(*app,*this); | |||
app->main(matched); | |||
} | |||
catch(...) { | |||
return translate_exception(); | |||
} | |||
} | |||
booster::intrusive_ptr<application> app = d->pool->get(service()); | |||
if(!app) | |||
return 500; | |||
try { | |||
context_guard g(*app,*this); | |||
app->main(matched); | |||
} | |||
catch(...) { | |||
return translate_exception(); | |||
} | |||
int status = request().on_content_start(); | |||
if(status!=0) | |||
return status; | |||
d->pool.swap(pool); | |||
d->app.swap(app); | |||
d->matched.swap(matched); | |||
d->app.swap(app); | |||
return 0; | |||
} | |||
bool context::has_file_filter() | |||
int context::on_content_progress(size_t n) | |||
{ | |||
return dynamic_cast<multipart_filter *>(request().content_filter())!=0; | |||
} | |||
int context::send_to_file_filter(file &f,int stage) | |||
{ | |||
try { | |||
context_guard g(*d->app,*this); | |||
multipart_filter *filter=static_cast<multipart_filter *>(request().content_filter()); | |||
switch(stage) { | |||
case 0: filter->on_new_file(f); break; | |||
case 1: filter->on_upload_progress(f); break; | |||
case 2: filter->on_data_ready(f); break; | |||
} | |||
} | |||
catch(...) { | |||
return translate_exception(); | |||
} | |||
return 0; | |||
context_guard g(*d->app,*this); | |||
return request().on_content_progress(n); | |||
} | |||
void context::on_request_ready(bool error) | |||
@@ -272,33 +262,12 @@ void context::on_request_ready(bool error) | |||
booster::intrusive_ptr<application> app; | |||
pool.swap(d->pool); | |||
app.swap(d->app); | |||
basic_content_filter *filter = 0; | |||
if(error && app && (filter=request().content_filter())!=0) { | |||
context_guard g(*app,*this); | |||
try { | |||
filter->on_error(); | |||
} | |||
catch(...) {} | |||
if(error) { | |||
request().on_error(); | |||
return; | |||
} | |||
if(error) | |||
return; | |||
request().set_ready(); | |||
if(app && filter) { | |||
context_guard g(*app,*this); | |||
try { | |||
filter->on_end_of_content(); | |||
} | |||
catch(...) { | |||
translate_exception(); | |||
return; | |||
} | |||
} | |||
if(app) { | |||
app->assign_context(self()); | |||
dispatch(app,d->matched,false); | |||
@@ -15,6 +15,7 @@ | |||
#include <cppcms/util.h> | |||
#include "cached_settings.h" | |||
#include <cppcms/service.h> | |||
#include "multipart_parser.h" | |||
#include <stdio.h> | |||
#include <string.h> | |||
@@ -23,7 +24,7 @@ | |||
namespace cppcms { namespace http { | |||
using cppcms::impl::multipart_parser; | |||
namespace { | |||
template<typename Iterator> | |||
void skip_after_period(Iterator &p,Iterator e) | |||
@@ -130,8 +131,26 @@ struct request::_data { | |||
std::vector<char> post_data; | |||
content_limits limits; | |||
basic_content_filter *filter; | |||
bool filter_is_raw_content_filter; | |||
bool filter_is_multipart_filter; | |||
bool ready; | |||
_data(cppcms::service &srv) : limits(srv.cached_settings()),filter(0),ready(false) {} | |||
long long content_length; | |||
long long read_size; | |||
bool read_full; | |||
bool no_on_error; | |||
booster::hold_ptr<cppcms::impl::multipart_parser> multipart_parser; | |||
_data(cppcms::service &srv) : | |||
limits(srv.cached_settings()), | |||
filter(0), | |||
filter_is_raw_content_filter(false), | |||
filter_is_multipart_filter(false), | |||
ready(false), | |||
content_length(0), | |||
read_size(0), | |||
read_full(false), | |||
no_on_error(false) | |||
{ | |||
} | |||
}; | |||
basic_content_filter *request::content_filter() | |||
@@ -142,55 +161,221 @@ basic_content_filter *request::content_filter() | |||
void request::content_filter(basic_content_filter *filter) | |||
{ | |||
d->filter = filter; | |||
d->filter_is_multipart_filter = dynamic_cast<multipart_filter *>(filter) != 0; | |||
d->filter_is_raw_content_filter = dynamic_cast<raw_content_filter *>(filter) != 0; | |||
} | |||
bool request::is_ready() | |||
{ | |||
return d->ready; | |||
} | |||
void request::set_ready() | |||
{ | |||
d->ready = true; | |||
} | |||
void request::set_post_data(std::vector<char> &post_data) | |||
std::pair<char *,size_t > request::get_buffer() | |||
{ | |||
d->post_data.clear(); | |||
d->post_data.swap(post_data); | |||
if(content_type_.is_form_urlencoded()) { | |||
if(!d->post_data.empty()) { | |||
char const *pdata=&d->post_data.front(); | |||
parse_form_urlencoded(pdata,pdata+d->post_data.size(),post_); | |||
char *ptr = 0; | |||
size_t size = 0; | |||
if(d->read_full) { | |||
ptr = &d->post_data[0] + d->read_size; | |||
size = d->post_data.size() - d->read_size; | |||
} | |||
else { | |||
long long reminder = d->content_length - d->read_size; | |||
if(static_cast<long long>(d->post_data.size()) < reminder) { | |||
d->post_data.resize(d->content_length); | |||
} | |||
if(d->post_data.size() == 0) { | |||
std::vector<char> tmp; | |||
tmp.swap(d->post_data); | |||
} | |||
else { | |||
ptr = &d->post_data[0]; | |||
size = d->post_data.size(); | |||
} | |||
} | |||
return std::make_pair(ptr,size); | |||
} | |||
bool request::size_ok(file &f,long long size) | |||
{ | |||
if(!f.has_mime() && f.size() > size) { | |||
BOOSTER_NOTICE("cppcms") << "multipart/form-data non-file entry size too big " << | |||
f.size() | |||
<< " REMOTE_ADDR = `" << getenv("REMOTE_ADDR") | |||
<< "' REMOTE_HOST=`" << getenv("REMOTE_HOST") << "'"; | |||
return false; | |||
} | |||
return true; | |||
} | |||
namespace { | |||
std::string read_file(std::istream &in) | |||
std::string read_file(size_t reserve,std::istream &in) | |||
{ | |||
std::string res; | |||
while(in.good() && !in.eof()) { | |||
char buf[256]; | |||
in.read(buf,256); | |||
res.append(buf,in.gcount()); | |||
res.reserve(reserve); | |||
in.seekg(0); | |||
std::streambuf *buf = in.rdbuf(); | |||
int c; | |||
while((c=buf->sbumpc())!=EOF) { | |||
res+=char(c); | |||
} | |||
return res; | |||
} | |||
} | |||
void request::set_post_data(std::vector<booster::shared_ptr<file> > const &multipart) | |||
int request::on_content_progress(size_t n) | |||
{ | |||
for(unsigned i=0;i<multipart.size();i++) { | |||
if(!multipart[i]->has_mime()) { | |||
post_.insert(std::make_pair(multipart[i]->name(),read_file(multipart[i]->data()))); | |||
if(n==0) | |||
return 0; | |||
char const *begin = &d->post_data[0]; | |||
char const *end = begin + n; | |||
d->read_size += n; | |||
try { | |||
if(d->filter_is_raw_content_filter) { | |||
static_cast<raw_content_filter *>(d->filter)->on_data_chunk(&d->post_data[0],n); | |||
} | |||
else { | |||
files_.push_back(multipart[i]); | |||
if(d->multipart_parser.get()) { | |||
multipart_parser::parsing_result_type r = multipart_parser::continue_input; | |||
long long allowed=d->limits.content_length_limit(); | |||
while(begin!=end) { | |||
r = d->multipart_parser->consume(begin,end); | |||
switch(r) { | |||
case multipart_parser::meta_ready: | |||
{ | |||
if(d->filter_is_multipart_filter) { | |||
file &f=d->multipart_parser->get_file(); | |||
static_cast<multipart_filter *>(d->filter)->on_new_file(f); | |||
} | |||
} | |||
break; | |||
case multipart_parser::content_partial: | |||
{ | |||
file &f=d->multipart_parser->get_file(); | |||
if(!size_ok(f,allowed)) | |||
return 413; | |||
if(d->filter_is_multipart_filter) | |||
static_cast<multipart_filter *>(d->filter)->on_upload_progress(f); | |||
} | |||
break; | |||
case multipart_parser::content_ready: | |||
{ | |||
file &f=d->multipart_parser->last_file(); | |||
f.data().seekg(0); | |||
if(!size_ok(f,allowed)) | |||
return 413; | |||
if(d->filter_is_multipart_filter) | |||
static_cast<multipart_filter *>(d->filter)->on_data_ready(f); | |||
} | |||
break; | |||
case multipart_parser::continue_input: | |||
break; | |||
case multipart_parser::no_room_left: | |||
return 413; | |||
case multipart_parser::eof: | |||
if(begin!=end) | |||
return 400; | |||
if(d->read_size != d->content_length) | |||
return 400; | |||
break; | |||
case multipart_parser::parsing_error: | |||
default: | |||
return 400; | |||
} | |||
} | |||
if(begin==end && d->read_size==d->content_length && r!=multipart_parser::eof) { | |||
return 400; | |||
} | |||
} | |||
if(d->read_size == d->content_length) { | |||
if(d->read_full) { | |||
if(content_type_.is_form_urlencoded()) { | |||
char const *data = &d->post_data[0]; | |||
char const *data_end = data + d->post_data.size(); | |||
parse_form_urlencoded(data,data_end,post_); | |||
} | |||
} | |||
else { | |||
std::vector<char> tmp; | |||
tmp.swap(d->post_data); | |||
} | |||
if(d->filter) | |||
d->filter->on_end_of_content(); | |||
if(d->multipart_parser.get()) { | |||
multipart_parser::files_type mp=d->multipart_parser->get_files(); | |||
d->multipart_parser.reset(); | |||
for(multipart_parser::files_type::iterator p=mp.begin();p!=mp.end();++p) { | |||
multipart_parser::file_ptr f=*p; | |||
if(!f->has_mime()) | |||
post_.insert(std::make_pair(f->name(),read_file(f->size(),f->data()))); | |||
else | |||
files_.push_back(f); | |||
} | |||
} | |||
d->ready = true; | |||
} | |||
} | |||
catch(abort_upload const &ab) { | |||
d->no_on_error=true; | |||
return ab.code(); | |||
} | |||
catch(std::exception const &e) { | |||
BOOSTER_ERROR("cppcms") << e.what() << booster::trace(e); | |||
d->no_on_error=true; | |||
return 500; | |||
} | |||
catch(...) { | |||
BOOSTER_ERROR("cppcms") << "Unknown exception "; | |||
d->no_on_error=true; | |||
return 500; | |||
} | |||
return 0; | |||
} | |||
void request::on_error() | |||
{ | |||
if(d->filter) { | |||
d->filter->on_error(); | |||
} | |||
} | |||
int request::on_content_start() | |||
{ | |||
if(d->content_length == 0) | |||
return 0; | |||
if(content_type_.is_multipart_form_data()) { | |||
if(d->content_length > d->limits.multipart_form_data_limit()) | |||
return 413; | |||
} | |||
else { | |||
if(d->content_length > static_cast<long long>(d->limits.content_length_limit())) | |||
return 413; | |||
} | |||
if(!d->filter_is_raw_content_filter && !content_type_.is_multipart_form_data()) { | |||
d->post_data.resize(d->content_length); | |||
d->read_full = true; | |||
} | |||
else { | |||
if(d->content_length < 65535) | |||
d->post_data.resize(d->content_length); | |||
else | |||
d->post_data.resize(65536); | |||
if(content_type_.is_multipart_form_data() && !d->filter_is_raw_content_filter) { | |||
d->multipart_parser.reset(new multipart_parser( | |||
d->limits.uploads_path(), | |||
d->limits.file_in_memory_limit())); | |||
if(!d->multipart_parser->set_content_type(content_type_)) { | |||
return 400; | |||
} | |||
} | |||
} | |||
return 0; | |||
} | |||
bool request::parse_form_urlencoded(char const *begin,char const *end,form_type &out) | |||
{ | |||
@@ -215,6 +400,11 @@ bool request::prepare() | |||
get_.clear(); | |||
} | |||
parse_cookies(); | |||
char const *s = conn_->cgetenv("CONTENT_LENGTH"); | |||
if(!s || *s==0) | |||
d->content_length = 0; | |||
else | |||
d->content_length = atoll(s); | |||
content_type_ = cppcms::http::content_type(conn_->cgetenv("CONTENT_TYPE")); | |||
return true; | |||
} | |||
@@ -261,7 +451,7 @@ cppcms::http::content_type request::content_type_parsed() | |||
} | |||
std::string request::auth_type() { return conn_->getenv("AUTH_TYPE"); } | |||
unsigned long long request::content_length() { return atoll(conn_->getenv("CONTENT_LENGTH").c_str()); } | |||
unsigned long long request::content_length() { return d->content_length; } | |||
std::string request::content_type() { return conn_->getenv("CONTENT_TYPE"); } | |||
std::string request::gateway_interface(){ return conn_->getenv("GATEWAY_INTERFACE"); } | |||
std::string request::path_info() { return conn_->getenv("PATH_INFO"); } | |||
@@ -138,6 +138,7 @@ struct random_consumer { | |||
buffer+=block; | |||
size-=block; | |||
if(res==cppcms::impl::multipart_parser::eof) { | |||
TEST(start == end); | |||
*files = parser->get_files(); | |||
return res; | |||
} | |||