Browse Source

Unit test updates and API changes in multi-part parser to handle each file separatly

master
Artyom Beilis 8 years ago
parent
commit
62720419d6
3 changed files with 90 additions and 30 deletions
  1. +33
    -10
      private/multipart_parser.h
  2. +10
    -1
      src/cgi_api.cpp
  3. +47
    -19
      tests/multipart_parser_test.cpp

+ 33
- 10
private/multipart_parser.h View File

@@ -58,7 +58,8 @@ namespace cppcms {
state_ ( expecting_first_boundary ),
position_(0),
temp_dir_(temp_dir),
memory_limit_(memory_limit)
memory_limit_(memory_limit),
file_is_ready_(false)
{
}
~multipart_parser()
@@ -67,15 +68,31 @@ namespace cppcms {
typedef enum {
parsing_error,
meta_ready,
content_partial,
content_ready,
continue_input,
got_something,
eof,
no_room_left
} parsing_result_type;

parsing_result_type consume(char const *buffer,int size)
bool has_file() { return file_is_ready_; }
http::file &get_file()
{
for(;size > 0;buffer++,size--) {
if(!file_is_ready_)
throw booster::logic_error("Invalid state for get_file");
return *file_;
}
http::file &last_file()
{
if(files_.empty())
throw booster::logic_error("No file was uploaded");
return *files_.back();
}

parsing_result_type consume(char const *&buffer,char const *buffer_end)
{
for(;buffer!=buffer_end;buffer++) {
#ifdef DEBUG_MULTIPART_PARSER
std::cerr << "[";
if(*buffer < 32)
@@ -115,7 +132,7 @@ namespace cppcms {
case expecting_eof_lf:
if(*buffer!='\n')
return parsing_error;
if(size == 1)
if(buffer + 1 == buffer_end)
return eof;
else
return parsing_error;
@@ -136,6 +153,9 @@ namespace cppcms {
header_.clear();
position_ = 0;
state_ = expecting_separator_boundary;
buffer++;
file_is_ready_=true;
return meta_ready;
}
break;
case expecting_separator_boundary:
@@ -144,7 +164,7 @@ namespace cppcms {
char const *this_boundary = boundary_.c_str();
size_t boundary_size = boundary_.size();
size_t added = 0;
while(size > 0) {
while(buffer != buffer_end) {
char c=*buffer;
if(c == this_boundary[position_])
position_++;
@@ -175,19 +195,21 @@ namespace cppcms {
if(memory_limit_ != -1) {
file_->set_memory_limit(memory_limit_);
}
break;
buffer++;
file_is_ready_=false;
return content_ready;
}
buffer++;
size--;
} // end while
file_->add_bytes_to_size(added);
added = 0;
return content_partial;
}
break;
}
}
if(!files_.empty())
return got_something;
if(file_is_ready_)
return content_partial;
else
return continue_input;
}
@@ -317,6 +339,7 @@ namespace cppcms {

std::string temp_dir_;
int memory_limit_;
bool file_is_ready_;
};

#ifdef DEBUG_MULTIPART_PARSER


+ 10
- 1
src/cgi_api.cpp View File

@@ -359,7 +359,16 @@ void connection::on_some_multipart_read(booster::system::error_code const &e,siz
if(e) { set_error(h,e.message()); return; }
read_size_-=n;
if(read_size_ < 0) { handle_http_error(400,context,h); return ;}
multipart_parser::parsing_result_type r = multipart_parser_->consume(&content_.front(),n);
char const *begin = &content_.front();
char const *end = begin + n;
multipart_parser::parsing_result_type r = multipart_parser::continue_input;
while(begin!=end) {
r = multipart_parser_->consume(begin,end);
if(r==multipart_parser::meta_ready || r == multipart_parser::content_ready || r==multipart_parser::content_partial)
continue;
break;
}

if(r == multipart_parser::eof) {
if(read_size_ != 0) {
handle_http_error(400,context,h);


+ 47
- 19
tests/multipart_parser_test.cpp View File

@@ -66,6 +66,8 @@ std::string getcontent(std::istream &in)
if(in.gcount() == 1)
res+=c;
}
in.clear();
in.seekg(0);
return res;
}

@@ -82,15 +84,17 @@ std::string getcontent(booster::shared_ptr<cppcms::http::file> const &p)
}

struct random_consumer {
random_consumer(int bs,cppcms::impl::multipart_parser &p,cppcms::impl::multipart_parser::files_type &f) :
random_consumer(int bs,cppcms::impl::multipart_parser &p,cppcms::impl::multipart_parser::files_type &f,std::vector<std::string> *cont = 0) :
block_size(bs),
parser(&p),
files(&f)
files(&f),
strings(cont)
{
}
int block_size;
cppcms::impl::multipart_parser *parser;
cppcms::impl::multipart_parser::files_type *files;
std::vector<std::string> *strings;
cppcms::impl::multipart_parser::parsing_result_type operator()(char const *buffer,int size)
{
while(size > 0) {
@@ -103,28 +107,48 @@ struct random_consumer {
if(block > size)
block=size;
cppcms::impl::multipart_parser::parsing_result_type res = parser->consume(buffer,block);
#ifdef DEBUG_MULTIPART_PARSER
std::cerr << "Got " << int(res) << " Consumed " << block << std::endl;
#endif
cppcms::impl::multipart_parser::parsing_result_type res = cppcms::impl::multipart_parser::continue_input;
char const *start = buffer;
char const *end = start + block;
while(start != end) {
res = parser->consume(start,end);
TEST(start<=end);
if(res == cppcms::impl::multipart_parser::meta_ready) {
TEST(parser->has_file());
TEST(!parser->get_file().name().empty());
TEST(parser->get_file().size()==0);
continue;
}
else if(res == cppcms::impl::multipart_parser::content_partial) {
TEST(parser->has_file());
TEST(!parser->get_file().name().empty());
continue;
}
else if(res == cppcms::impl::multipart_parser::content_ready) {
TEST(!parser->has_file());
if(strings)
strings->push_back(getcontent(parser->last_file()));
continue;
}
else
break;
}
buffer+=block;
size-=block;
if(res==cppcms::impl::multipart_parser::eof) {
cppcms::impl::multipart_parser::files_type fblock = parser->get_files();
for(unsigned i=0;i<fblock.size();i++) {
files->push_back(fblock[i]);
}
*files = parser->get_files();
return res;
}
if(res==cppcms::impl::multipart_parser::parsing_error)
if(res==cppcms::impl::multipart_parser::parsing_error) {
return res;
if(res==cppcms::impl::multipart_parser::got_something) {
cppcms::impl::multipart_parser::files_type fblock = parser->get_files();
TEST(!fblock.empty());
for(unsigned i=0;i<fblock.size();i++) {
files->push_back(fblock[i]);
}
}
TEST( res==cppcms::impl::multipart_parser::continue_input
|| res==cppcms::impl::multipart_parser::meta_ready
|| res==cppcms::impl::multipart_parser::content_partial
|| res==cppcms::impl::multipart_parser::content_ready
);
}
TEST(!"Never get there");
return cppcms::impl::multipart_parser::eof;
@@ -139,7 +163,7 @@ int main(int argc,char **argv)
return 1;
}
try {
int block_size[]={ 1, -1, 5, 3, 10 };
int block_size[]={ 1, -1, 5, 3, 10, 1000 };
int max_mem_size[] = { 0, 3, 10000 };
for(unsigned i=0;i<sizeof(block_size)/sizeof(block_size[0]);i++) {
for(unsigned j=0;j<sizeof(max_mem_size)/sizeof(max_mem_size[0]);j++) {
@@ -148,7 +172,8 @@ int main(int argc,char **argv)
cppcms::impl::multipart_parser parser("",max_mem_size[j]);
TEST(parser.set_content_type(std::string(test_1_content)));
cppcms::impl::multipart_parser::files_type files;
random_consumer c(block_size[i],parser,files);
std::vector<std::string> cts;
random_consumer c(block_size[i],parser,files,&cts);
TEST(c(test_1_file,strlen(test_1_file))==cppcms::impl::multipart_parser::eof);
TEST(files.size()==7);
TEST(files[0]->name()=="test1");
@@ -156,15 +181,18 @@ int main(int argc,char **argv)
TEST(files[0]->mime()=="text/plain");
std::string content = getcontent(files[0]);
TEST(content=="hello\r\n");
TEST(cts.at(0) == content);
TEST(files[1]->name()=="test2");
TEST(files[1]->filename()=="");
TEST(files[1]->mime()=="");
TEST(getcontent(files[1])=="שלום");
TEST(cts.at(1)=="שלום");
TEST(getcontent(files[2])=="x\r");
TEST(getcontent(files[3])=="x\r\n-");
TEST(getcontent(files[4])=="x\r\n--");
TEST(getcontent(files[5])=="x\r\n--x");
TEST(getcontent(files[6])=="x\r\n--x-");
TEST(cts.at(6)=="x\r\n--x-");
}
{
cppcms::impl::multipart_parser parser("",max_mem_size[j]);


Loading…
Cancel
Save