@@ -833,6 +833,10 @@ add_test(pool_test | |||
pool_test "-c" "${CNF}/pool_test.js" | |||
"--test-exec=${PYTHON} ${CNF}/pool_test.py") | |||
add_test(filter_test | |||
filter_test "-c" "${CNF}/filter_test.js" | |||
"--test-exec=${PYTHON} ${CNF}/filter_test.py") | |||
add_test(async_status_test | |||
status_test "-c" "${CNF}/status_test.js" "--test-async=async" | |||
"--test-exec=${PYTHON} ${CNF}/status_test.py async") | |||
@@ -30,37 +30,78 @@ namespace http { | |||
class CPPCMS_API abort_upload : public cppcms_error { | |||
public: | |||
/// | |||
/// Abort | |||
/// Abort upload progress, thrown from classes derived from basic_content_filter | |||
/// to abort the upload progress. status_code is the HTTP error code returned, for example 413 | |||
/// requested entity is too large | |||
/// | |||
abort_upload(int status_code); | |||
virtual ~abort_upload() throw(); | |||
/// | |||
/// Get the code | |||
/// | |||
int code() const; | |||
private: | |||
int code_; | |||
}; | |||
/// | |||
/// Class that represent the limits on the input content sizes | |||
/// | |||
class CPPCMS_API content_limits : public booster::noncopyable { | |||
friend class request; | |||
public: | |||
/// \cond INTERNAL | |||
content_limits(impl::cached_settings const &); | |||
/// \endcond | |||
content_limits(); | |||
~content_limits(); | |||
/// \endcond | |||
/// | |||
/// Get the size limit in bytes of any non multipart/form-data content | |||
/// | |||
/// Note form fields without content-type would be limited by this | |||
/// size even if the multipart_form_data_limit is much larger | |||
/// | |||
long long content_length_limit() const; | |||
/// | |||
/// Set the size limit of any non multipart/form-data content | |||
/// | |||
/// Note form fields without content-type would be limited by this | |||
/// size even if the multipart_form_data_limit is much larger | |||
/// | |||
void content_length_limit(long long size); | |||
/// | |||
/// Get the size limit of multipart/form-data content in bytes | |||
/// | |||
/// Note form fields without content-type would be limited by content_length_limit | |||
/// size even if the multipart_form_data_limit is much larger | |||
/// | |||
long long multipart_form_data_limit() const; | |||
/// | |||
/// Set the size limit of multipart/form-data content in bytes | |||
/// | |||
/// Note form fields without content-type would be limited by content_length_limit | |||
/// size even if the multipart_form_data_limit is much larger | |||
/// | |||
void multipart_form_data_limit(long long size); | |||
/// | |||
/// Get the maximal size of file that is still hold in memory rather than disk | |||
/// | |||
size_t file_in_memory_limit() const; | |||
/// | |||
/// Set the maximal size of file that is still hold in memory rather than disk | |||
/// | |||
void file_in_memory_limit(size_t size); | |||
/// | |||
/// Get a location of a temporary directory that files are uploaded to, if empty system default is used | |||
/// | |||
std::string uploads_path() const; | |||
/// | |||
/// Set a location of a temporary directory that files are uploaded to, if empty system default is used | |||
/// | |||
void uploads_path(std::string const &path); | |||
private: | |||
@@ -200,6 +200,10 @@ namespace cppcms { | |||
booster::hold_ptr<T> p; | |||
}; | |||
public: | |||
/// | |||
/// Get context specific value of type T binded to context. If none is stored or | |||
/// type mismatched NULL is returned | |||
/// | |||
template<typename T> | |||
T *get_specific() | |||
{ | |||
@@ -208,6 +212,9 @@ namespace cppcms { | |||
return 0; | |||
return sh->p.get(); | |||
} | |||
/// | |||
/// Reset context specific value of type T binded to context. Old value is deleted | |||
/// | |||
template<typename T> | |||
void reset_specific(T *ptr = 0) | |||
{ | |||
@@ -224,6 +231,9 @@ namespace cppcms { | |||
set_holder(sh); | |||
} | |||
} | |||
/// | |||
/// Release context specific value binded to context. | |||
/// | |||
template<typename T> | |||
T *release_specific() | |||
{ | |||
@@ -76,6 +76,13 @@ namespace http { | |||
/// Make sure that file created by output_file member function is not removed in destructor | |||
/// | |||
void make_permanent(); | |||
/// | |||
/// Close the file if it is still open, if the file temporary it is deleted, the the | |||
/// file in memory its content is removed, data() would return non-usable stream | |||
/// | |||
/// Returns 0 in case of sucess and -1 in case of failure | |||
/// | |||
int close(); | |||
/// | |||
/// Save file to file named \a filename. Throws cppcms_error in case of failure. | |||
@@ -102,6 +102,11 @@ public: | |||
} | |||
f_ = 0; | |||
} | |||
setp(0,0); | |||
setg(0,0,0); | |||
clear(input_); | |||
clear(output_); | |||
clear(data_); | |||
return 0; | |||
} | |||
#ifdef DEBUG_FILE_BUFFER | |||
@@ -152,14 +152,23 @@ file::file() : | |||
{ | |||
} | |||
file::~file() | |||
int file::close() | |||
{ | |||
if(!d->fb.in_memory() && !removed_) { | |||
d->fb.close(); | |||
int r = d->fb.close(); | |||
if(file_temporary_ && !d->fb.name().empty()) { | |||
booster::nowide::remove(d->fb.name().c_str()); | |||
} | |||
return r; | |||
} | |||
else | |||
return d->fb.close(); | |||
} | |||
file::~file() | |||
{ | |||
close(); | |||
} | |||
void file::filename(std::string const &v) | |||
@@ -168,6 +168,11 @@ public: | |||
if(request().get("abort")=="on_headers_ready") | |||
do_abort(501); | |||
request().set_content_filter(*this); | |||
std::string cl_limit,mp_limit; | |||
if((cl_limit=request().get("cl_limit"))!="") | |||
request().limits().content_length_limit(atoi(cl_limit.c_str())); | |||
if((mp_limit=request().get("mp_limit"))!="") | |||
request().limits().multipart_form_data_limit(atoi(mp_limit.c_str())); | |||
} | |||
else { | |||
test_data *td = context().get_specific<test_data>(); | |||
@@ -193,6 +198,7 @@ public: | |||
} | |||
}; | |||
int main(int argc,char **argv) | |||
{ | |||
try { | |||
@@ -31,9 +31,12 @@ def make_multipart_form_data(qs): | |||
r=[] | |||
fc={} | |||
ln={} | |||
post={} | |||
for item in qs: | |||
key=item.split('=')[0] | |||
value = item.split('=')[1] | |||
if key=='formdata': | |||
post[value]=True | |||
pr=key[0:2] | |||
if pr == 'f_' or pr == 'l_': | |||
name = key[2:] | |||
@@ -46,7 +49,8 @@ def make_multipart_form_data(qs): | |||
l=ln[name] | |||
c=fc[name] | |||
r.append('--123456\r\n') | |||
r.append('Content-Type: text/plain\r\n') | |||
if not name in post: | |||
r.append('Content-Type: text/plain\r\n') | |||
r.append('Content-Disposition: form-data; name="' + name +'"\r\n\r\n') | |||
r.append(make_content(c,l)) | |||
r.append('\r\n') | |||
@@ -186,14 +190,22 @@ def test_upload(): | |||
test(r['status']==400) | |||
test_on_error_called() | |||
r=transfer('/upload',['l_1=100','f_1=a','save_to_1=test.txt']) | |||
test(r['status']==200) | |||
try: | |||
os.remove('test.txt') | |||
except: | |||
pass | |||
r=transfer('/upload',['l_1=100','f_1=a','save_to_1=test.txt']) | |||
test(r['status']==200) | |||
time.sleep(0.2); | |||
test(open('test.txt','rb').read() == make_content('a',100)) | |||
os.remove('test.txt') | |||
test(transfer('/upload',['l_1=100','f_1=a','cl_limit=5'])['status']==200) | |||
test(transfer('/upload',['fail=1','cl_limit=5'],{'content':'{"x":1000}','content_type':'application/json'})['status']==413) | |||
test(transfer('/upload',['fail=1','l_1=100','f_1=a','mp_limit=50'])['status']==413) | |||
test(transfer('/upload',['l_1=100','f_1=a','mp_limit=200'])['status']==200) | |||
test(transfer('/upload',['formdata=1','l_1=100','f_1=a','l_2=200','f_2=b','mp_limit=500','cl_limit=100'])['status']==200) | |||
test(transfer('/upload',['fail=1','formdata=1','l_1=100','f_1=a','l_2=200','f_2=b','mp_limit=500','cl_limit=99'])['status']==413) | |||
test_upload() | |||
print "OK" |