Added some HTTP parser unit tests, fixed HTTP support for zero content length, added gcovr file for coverage reports
This commit is contained in:
		
							parent
							
								
									9a599a06de
								
							
						
					
					
						commit
						8b8a28c4ec
					
				
					 5 changed files with 89 additions and 3 deletions
				
			
		
							
								
								
									
										3
									
								
								gcovr.cfg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								gcovr.cfg
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
filter = src/
 | 
			
		||||
filter = lib/
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +45,7 @@ void HTTP::Parser::CleanPreserveHeaders(){
 | 
			
		|||
  protocol = "HTTP/1.1";
 | 
			
		||||
  body.clear();
 | 
			
		||||
  length = 0;
 | 
			
		||||
  knownLength = false;
 | 
			
		||||
  vars.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -619,11 +620,13 @@ bool HTTP::Parser::parse(std::string &HTTPbuffer, Util::DataCallback &cb){
 | 
			
		|||
        if (tmpA.size() == 0){
 | 
			
		||||
          seenHeaders = true;
 | 
			
		||||
          body.clear();
 | 
			
		||||
          knownLength = false;
 | 
			
		||||
          if (GetHeader("Content-Length") != ""){
 | 
			
		||||
            length = atoi(GetHeader("Content-Length").c_str());
 | 
			
		||||
            if (!bodyCallback && (&cb == &Util::defaultDataCallback) && body.capacity() < length){
 | 
			
		||||
              body.reserve(length);
 | 
			
		||||
            }
 | 
			
		||||
            knownLength = true;
 | 
			
		||||
          }
 | 
			
		||||
          if (GetHeader("Transfer-Encoding") == "chunked"){
 | 
			
		||||
            getChunks = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -645,7 +648,7 @@ bool HTTP::Parser::parse(std::string &HTTPbuffer, Util::DataCallback &cb){
 | 
			
		|||
        unsigned int code = atoi(url.data());
 | 
			
		||||
        if ((code >= 100 && code < 200) || code == 204 || code == 304){return true;}
 | 
			
		||||
      }
 | 
			
		||||
      if (length > 0 && !getChunks){
 | 
			
		||||
      if (knownLength && !getChunks){
 | 
			
		||||
        unsigned int toappend = length - body.length();
 | 
			
		||||
 | 
			
		||||
        // limit the amount of bytes that will be appended to the amount there
 | 
			
		||||
| 
						 | 
				
			
			@ -673,8 +676,8 @@ bool HTTP::Parser::parse(std::string &HTTPbuffer, Util::DataCallback &cb){
 | 
			
		|||
          currentLength += toappend;
 | 
			
		||||
        }
 | 
			
		||||
        if (length == body.length()){
 | 
			
		||||
          // parse POST variables
 | 
			
		||||
          if (method == "POST"){parseVars(body, vars);}
 | 
			
		||||
          // parse POST body if the content type is URLEncoded
 | 
			
		||||
          if (method == "POST" && GetHeader("Content-Type") == "application/x-www-form-urlencoded"){parseVars(body, vars);}
 | 
			
		||||
          return true;
 | 
			
		||||
        }else{
 | 
			
		||||
          return false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,6 +57,7 @@ namespace HTTP{
 | 
			
		|||
    std::string url;
 | 
			
		||||
    std::string protocol;
 | 
			
		||||
    unsigned int length;
 | 
			
		||||
    bool knownLength;
 | 
			
		||||
    unsigned int currentLength;
 | 
			
		||||
    bool headerOnly; ///< If true, do not parse body if the length is a known size.
 | 
			
		||||
    bool bufferChunks;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										64
									
								
								test/http_parser.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								test/http_parser.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,64 @@
 | 
			
		|||
#include <iostream>
 | 
			
		||||
#include <mist/http_parser.h>
 | 
			
		||||
#include <mist/timing.h>
 | 
			
		||||
 | 
			
		||||
int main(int argc, char ** argv){
 | 
			
		||||
  bool preMade = false;
 | 
			
		||||
  Socket::Connection C(1, 0); // Open stdio by default
 | 
			
		||||
  // If there is a T_HTTP environment variable, use that as input instead
 | 
			
		||||
  if (getenv("T_HTTP")){
 | 
			
		||||
    preMade = true;
 | 
			
		||||
    // Keep stdio open, only drop the reference to it
 | 
			
		||||
    C.drop();
 | 
			
		||||
    // Create a pipe and reconnect the socket to it
 | 
			
		||||
    int p[2];
 | 
			
		||||
    if (pipe(p)){
 | 
			
		||||
      FAIL_MSG("Could not open pipe!");
 | 
			
		||||
      return 1;
 | 
			
		||||
    }
 | 
			
		||||
    C.open(p[1], p[0]);
 | 
			
		||||
    // Write the T_HTTP env contents into the pipe
 | 
			
		||||
    C.SendNow(getenv("T_HTTP"));
 | 
			
		||||
    // Close the write end if we're not lingering
 | 
			
		||||
    if (!getenv("T_LINGER")){close(p[1]);}
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  HTTP::Parser p;
 | 
			
		||||
  int counter = 0;
 | 
			
		||||
  C.setBlocking(false);
 | 
			
		||||
  uint64_t lastData = Util::bootMS();
 | 
			
		||||
  do {
 | 
			
		||||
    if (C.spool()){
 | 
			
		||||
      lastData = Util::bootMS();
 | 
			
		||||
      while (p.Read(C)){
 | 
			
		||||
        INFO_MSG("Read a HTTP message: %s %s %s (%zu bytes)", p.method.c_str(), p.url.c_str(), p.protocol.c_str(), p.body.size());
 | 
			
		||||
        ++counter;
 | 
			
		||||
        p.Clean();
 | 
			
		||||
      }
 | 
			
		||||
    }else{
 | 
			
		||||
      // premade requests will instantly time out, others after 10 seconds
 | 
			
		||||
      if (preMade){break;}
 | 
			
		||||
      if (Util::bootMS() > lastData + 10000){
 | 
			
		||||
        WARN_MSG("Read timeout, aborting");
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      Util::sleep(5);
 | 
			
		||||
    }
 | 
			
		||||
  }while(C);
 | 
			
		||||
  while (p.Read(C)){
 | 
			
		||||
    INFO_MSG("Read a HTTP message: %s %s %s (%zu bytes)", p.method.c_str(), p.url.c_str(), p.protocol.c_str(), p.body.size());
 | 
			
		||||
    ++counter;
 | 
			
		||||
    p.Clean();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  INFO_MSG("Total messages: %d", counter);
 | 
			
		||||
 | 
			
		||||
  if (getenv("T_COUNT")){
 | 
			
		||||
    if (counter != atoi(getenv("T_COUNT"))){
 | 
			
		||||
      return 1;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -148,6 +148,21 @@ test('DTSC Sizing Test', dtsc_sizing_test)
 | 
			
		|||
bitwritertest = executable('bitwritertest', 'bitwriter.cpp', dependencies: libmist_dep)
 | 
			
		||||
test('bitWriter Test', bitwritertest)
 | 
			
		||||
 | 
			
		||||
httpparsertest = executable('httpparsertest', 'http_parser.cpp', dependencies: libmist_dep)
 | 
			
		||||
test('GET request for /', httpparsertest, suite: 'HTTP parser', env: {'T_HTTP':'GET / HTTP/1.1\n\n', 'T_COUNT':'1'})
 | 
			
		||||
test('GET request for / with carriage returns', httpparsertest, suite: 'HTTP parser', env: {'T_HTTP':'GET / HTTP/1.1\r\n\r\n', 'T_COUNT':'1'})
 | 
			
		||||
test('POST request to /, raw body', httpparsertest, suite: 'HTTP parser', env: {'T_HTTP':'POST / HTTP/1.1\nContent-Length: 4\nContent-Type: text/plain\n\ntest', 'T_COUNT':'1'})
 | 
			
		||||
test('POST request to /, urlencoded body', httpparsertest, suite: 'HTTP parser', env: {'T_HTTP':'POST / HTTP/1.1\nContent-Length: 28\nContent-Type: application/x-www-form-urlencoded\n\nfoo=bar&banana=sauce&cookies', 'T_COUNT':'1'})
 | 
			
		||||
test('Blank HTTP response, closed connection', httpparsertest, suite: 'HTTP parser', env: {'T_HTTP':'HTTP/1.1 200 OK\nDate: Thu, 15 Jun 2023 21:34:06 GMT\nContent-Length: 0\n\n', 'T_COUNT':'1'})
 | 
			
		||||
test('Blank HTTP response, lingering connection', httpparsertest, suite: 'HTTP parser', env: {'T_HTTP':'HTTP/1.1 200 OK\nDate: Thu, 15 Jun 2023 21:34:06 GMT\nContent-Length: 0\n\n', 'T_LINGER':'1', 'T_COUNT':'1'})
 | 
			
		||||
test('Simple HTTP response, closed connection', httpparsertest, suite: 'HTTP parser', env: {'T_HTTP':'HTTP/1.1 200 OK\nDate: Thu, 15 Jun 2023 21:34:06 GMT\nContent-Length: 4\n\ntest', 'T_COUNT':'1'})
 | 
			
		||||
test('Simple HTTP response, lingering connection', httpparsertest, suite: 'HTTP parser', env: {'T_HTTP':'HTTP/1.1 200 OK\nDate: Thu, 15 Jun 2023 21:34:06 GMT\nContent-Length: 4\n\ntest', 'T_LINGER':'1', 'T_COUNT':'1'})
 | 
			
		||||
test('Simple HTTP response, no length, closed connection', httpparsertest, suite: 'HTTP parser', env: {'T_HTTP':'HTTP/1.1 200 OK\nDate: Thu, 15 Jun 2023 21:34:06 GMT\n\ntest', 'T_COUNT':'1'})
 | 
			
		||||
test('Simple HTTP response, no length, lingering connection', httpparsertest, suite: 'HTTP parser', env: {'T_HTTP':'HTTP/1.1 200 OK\nDate: Thu, 15 Jun 2023 21:34:06 GMT\n\ntest', 'T_LINGER':'1', 'T_COUNT':'0'})
 | 
			
		||||
test('Chunked HTTP response, closed connection', httpparsertest, suite: 'HTTP parser', env: {'T_HTTP':'HTTP/1.1 200 OK\nTransfer-Encoding: chunked\n\n1\nt\n3\nest\n0\n\n', 'T_COUNT':'1'})
 | 
			
		||||
test('Chunked HTTP response, lingering connection', httpparsertest, suite: 'HTTP parser', env: {'T_HTTP':'HTTP/1.1 200 OK\nTransfer-Encoding: chunked\n\n1\nt\n3\nest\n0\n\n', 'T_LINGER':'1', 'T_COUNT':'1'})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#abst_test = executable('abst_test', 'abst_test.cpp', dependencies: libmist_dep)
 | 
			
		||||
#test('MP4::ABST Test', abst_test)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue