http_server : Fix and enable example tests

This introduces the following changes in the example test scripts :
* Dependency on python requests library removed in favor of httplib
* Bug fixed in the logic responsible for receiving and processing http chunked responses
* Default timeouts increased Note : Due to connectivity issues (between runner host and DUT) in the runner environment, some of the advanced_tests are being ignored. These tests are intended for verifying the expected limits of the http_server capabilities, and implement sending and receiving of large HTTP packets and malformed requests, running multiple parallel sessions, etc. It is advised that all these tests be run locally, when making changes or adding new features to this component.
This commit is contained in:
Anurag Kar
2018-08-10 23:58:38 +05:30
parent ba17648aea
commit e2da1d9905
6 changed files with 476 additions and 544 deletions
@@ -28,9 +28,9 @@ if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
# When running on local machine execute the following before running this script
# > export TEST_FW_PATH='~/esp/esp-idf/tools/tiny-test-fw'
# > make print_flash_cmd | tail -n 1 > build/download.config
# > make app bootloader
# > make print_flash_cmd | tail -n 1 > build/download.config
# > export TEST_FW_PATH=~/esp/esp-idf/tools/tiny-test-fw
import TinyFW
import IDF
@@ -39,7 +39,7 @@ import IDF
expath = os.path.dirname(os.path.realpath(__file__))
client = imp.load_source("client", expath + "/scripts/client.py")
@IDF.idf_example_test(env_tag="Example_WIFI", ignore=True)
@IDF.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_http_server_simple(env, extra_data):
# Acquire DUT
dut1 = env.get_dut("http_server", "examples/protocols/http_server/simple")
@@ -47,21 +47,23 @@ def test_examples_protocol_http_server_simple(env, extra_data):
# Get binary file
binary_file = os.path.join(dut1.app.binary_path, "simple.bin")
bin_size = os.path.getsize(binary_file)
IDF.log_performance("http_server_bin_size", "{}KB".format(bin_size//1024))
IDF.check_performance("http_server_bin_size", bin_size//1024)
IDF.log_performance("http_server_bin_size", "{}KB".format(bin_size/1024))
IDF.check_performance("http_server_bin_size", bin_size/1024)
# Upload binary and start testing
print "Starting http_server simple test app"
dut1.start_app()
# Parse IP address of STA
got_ip = dut1.expect(re.compile(r"(?:[\s\S]*)Got IP: (\d+.\d+.\d+.\d+)"), timeout=30)[0]
got_port = dut1.expect(re.compile(r"(?:[\s\S]*)Starting server on port: (\d+)"))[0]
print "Waiting to connect with AP"
got_ip = dut1.expect(re.compile(r"(?:[\s\S]*)Got IP: (\d+.\d+.\d+.\d+)"), timeout=120)[0]
got_port = dut1.expect(re.compile(r"(?:[\s\S]*)Starting server on port: (\d+)"), timeout=30)[0]
print "Got IP : " + got_ip
print "Got Port : " + got_port
# Expected Logs
dut1.expect("Registering URI handlers")
dut1.expect("Registering URI handlers", timeout=30)
# Run test script
# If failed raise appropriate exception
@@ -70,24 +72,24 @@ def test_examples_protocol_http_server_simple(env, extra_data):
raise RuntimeError
# Acquire host IP. Need a way to check it
host_ip = dut1.expect(re.compile(r"(?:[\s\S]*)Found header => Host: (\d+.\d+.\d+.\d+)"))[0]
host_ip = dut1.expect(re.compile(r"(?:[\s\S]*)Found header => Host: (\d+.\d+.\d+.\d+)"), timeout=30)[0]
# Match additional headers sent in the request
dut1.expect("Found header => Test-Header-2: Test-Value-2")
dut1.expect("Found header => Test-Header-1: Test-Value-1")
dut1.expect("Found URL query parameter => query1=value1")
dut1.expect("Found URL query parameter => query3=value3")
dut1.expect("Found URL query parameter => query2=value2")
dut1.expect("Request headers lost")
dut1.expect("Found header => Test-Header-2: Test-Value-2", timeout=30)
dut1.expect("Found header => Test-Header-1: Test-Value-1", timeout=30)
dut1.expect("Found URL query parameter => query1=value1", timeout=30)
dut1.expect("Found URL query parameter => query3=value3", timeout=30)
dut1.expect("Found URL query parameter => query2=value2", timeout=30)
dut1.expect("Request headers lost", timeout=30)
print "Test /ctrl PUT handler and realtime handler de/registration"
if not client.test_put_handler(got_ip, got_port):
raise RuntimeError
dut1.expect("Unregistering /hello and /echo URIs")
dut1.expect("Registering /hello and /echo URIs")
dut1.expect("Unregistering /hello and /echo URIs", timeout=30)
dut1.expect("Registering /hello and /echo URIs", timeout=30)
# Generate random data of 10KB
random_data = ''.join(string.printable[random.randint(0,len(string.printable))-1] for _ in range(1024*10))
random_data = ''.join(string.printable[random.randint(0,len(string.printable))-1] for _ in range(10*1024))
print "Test /echo POST handler with random data"
if not client.test_post_handler(got_ip, got_port, random_data):
raise RuntimeError
@@ -96,19 +98,19 @@ def test_examples_protocol_http_server_simple(env, extra_data):
print "Test /hello with custom query : " + query
if not client.test_custom_uri_query(got_ip, got_port, query):
raise RuntimeError
dut1.expect("Found URL query => " + query)
dut1.expect("Found URL query => " + query, timeout=30)
query = "abcd+1234%20xyz"
print "Test /hello with custom query : " + query
if not client.test_custom_uri_query(got_ip, got_port, query):
raise RuntimeError
dut1.expect("Found URL query => " + query)
dut1.expect("Found URL query => " + query, timeout=30)
query = "abcd\nyz"
print "Test /hello with invalid query"
if client.test_custom_uri_query(got_ip, got_port, query):
raise RuntimeError
dut1.expect("400 Bad Request - Server unable to understand request due to invalid syntax")
dut1.expect("400 Bad Request - Server unable to understand request due to invalid syntax", timeout=30)
if __name__ == '__main__':
test_examples_protocol_http_server_simple()
@@ -14,117 +14,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import socket
import httplib
import argparse
class Session:
def __init__(self, addr, port):
self.client = socket.create_connection((addr, int(port)))
self.target = addr
def send_get(self, path, headers=None):
request = "GET " + path + " HTTP/1.1\r\nHost: " + self.target
if headers:
for field, value in headers.iteritems():
request += "\r\n"+field+": "+value
request += "\r\n\r\n"
self.client.send(request);
def send_put(self, path, data, headers=None):
request = "PUT " + path + " HTTP/1.1\r\nHost: " + self.target
if headers:
for field, value in headers.iteritems():
request += "\r\n"+field+": "+value
request += "\r\nContent-Length: " + str(len(data)) +"\r\n\r\n"
self.client.send(request)
self.client.send(data)
def send_post(self, path, data, headers=None):
request = "POST " + path + " HTTP/1.1\r\nHost: " + self.target
if headers:
for field, value in headers.iteritems():
request += "\r\n"+field+": "+value
request += "\r\nContent-Length: " + str(len(data)) +"\r\n\r\n"
self.client.send(request)
self.client.send(data)
def read_resp_hdrs(self):
state = 'nothing'
resp_read = ''
while True:
char = self.client.recv(1)
if char == '\r' and state == 'nothing':
state = 'first_cr'
elif char == '\n' and state == 'first_cr':
state = 'first_lf'
elif char == '\r' and state == 'first_lf':
state = 'second_cr'
elif char == '\n' and state == 'second_cr':
state = 'second_lf'
else:
state = 'nothing'
resp_read += char
if state == 'second_lf':
break;
# Handle first line
line_hdrs = resp_read.splitlines()
line_comp = line_hdrs[0].split()
self.status = line_comp[1]
del line_hdrs[0]
self.encoding = ''
self.content_type = ''
headers = dict()
# Process other headers
for h in range(len(line_hdrs)):
line_comp = line_hdrs[h].split(':')
if line_comp[0] == 'Content-Length':
self.content_len = int(line_comp[1])
if line_comp[0] == 'Content-Type':
self.content_type = line_comp[1].lstrip()
if line_comp[0] == 'Transfer-Encoding':
self.encoding = line_comp[1].lstrip()
if len(line_comp) == 2:
headers[line_comp[0]] = line_comp[1].lstrip()
return headers
def read_resp_data(self):
read_data = ''
if self.encoding != 'chunked':
while len(read_data) != self.content_len:
read_data += self.client.recv(self.content_len)
self.content_len = 0
else:
chunk_data_buf = ''
while (True):
# Read one character into temp buffer
read_ch = self.client.recv(1)
# Check CRLF
if (read_ch == '\r'):
read_ch = self.client.recv(1)
if (read_ch == '\n'):
# If CRLF decode length of chunk
chunk_len = int(chunk_data_buf, 16)
# Keep adding to contents
self.content_len += chunk_len
read_data += self.client.recv(chunk_len)
chunk_data_buf = ''
# Fetch remaining CRLF
if self.client.recv(2) != "\r\n":
# Error in packet
return None
if not chunk_len:
# If last chunk
break
continue
chunk_data_buf += '\r'
# If not CRLF continue appending
# character to chunked data buffer
chunk_data_buf += read_ch
return read_data
def close(self):
self.client.close()
def verbose_print(verbosity, *args):
if (verbosity):
print ''.join(str(elems) for elems in args)
@@ -133,7 +25,7 @@ def test_get_handler(ip, port, verbosity = False):
verbose_print(verbosity, "======== GET HANDLER TEST =============")
# Establish HTTP connection
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
sess = Session(ip, port)
sess = httplib.HTTPConnection(ip + ":" + port)
uri = "/hello?query1=value1&query2=value2&query3=value3"
# GET hello response
@@ -142,13 +34,14 @@ def test_get_handler(ip, port, verbosity = False):
verbose_print(verbosity, "Sending additional headers : ")
for k, v in test_headers.iteritems():
verbose_print(verbosity, "\t", k, ": ", v)
sess.send_get(uri, test_headers)
resp_hdrs = sess.read_resp_hdrs()
resp_data = sess.read_resp_data()
sess.request("GET", url=uri, headers=test_headers)
resp = sess.getresponse()
resp_hdrs = resp.getheaders()
resp_data = resp.read()
try:
if resp_hdrs["Custom-Header-1"] != "Custom-Value-1":
if resp.getheader("Custom-Header-1") != "Custom-Value-1":
return False
if resp_hdrs["Custom-Header-2"] != "Custom-Value-2":
if resp.getheader("Custom-Header-2") != "Custom-Value-2":
return False
except:
return False
@@ -156,7 +49,7 @@ def test_get_handler(ip, port, verbosity = False):
verbose_print(verbosity, "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
verbose_print(verbosity, "Server response to GET /hello")
verbose_print(verbosity, "Response Headers : ")
for k, v in resp_hdrs.iteritems():
for k, v in resp_hdrs:
verbose_print(verbosity, "\t", k, ": ", v)
verbose_print(verbosity, "Response Data : " + resp_data)
verbose_print(verbosity, "========================================\n")
@@ -169,12 +62,12 @@ def test_post_handler(ip, port, msg, verbosity = False):
verbose_print(verbosity, "======== POST HANDLER TEST ============")
# Establish HTTP connection
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
sess = Session(ip, port)
sess = httplib.HTTPConnection(ip + ":" + port)
# POST message to /echo and get back response
sess.send_post("/echo", msg)
resp_hdrs = sess.read_resp_hdrs()
resp_data = sess.read_resp_data()
sess.request("POST", url="/echo", body=msg)
resp = sess.getresponse()
resp_data = resp.read()
verbose_print(verbosity, "Server response to POST /echo (" + msg + ")")
verbose_print(verbosity, "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
verbose_print(verbosity, resp_data)
@@ -188,30 +81,28 @@ def test_put_handler(ip, port, verbosity = False):
verbose_print(verbosity, "======== PUT HANDLER TEST =============")
# Establish HTTP connection
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
sess = Session(ip, port)
sess = httplib.HTTPConnection(ip + ":" + port)
# PUT message to /ctrl to disable /hello URI handler
verbose_print(verbosity, "Disabling /hello handler")
sess.send_put("/ctrl", "0")
sess.read_resp_hdrs()
if sess.content_len:
sess.read_resp_data()
sess.request("PUT", url="/ctrl", body="0")
resp = sess.getresponse()
resp.read()
sess.send_get("/hello")
sess.read_resp_hdrs()
resp_data1 = sess.read_resp_data()
sess.request("GET", url="/hello")
resp = sess.getresponse()
resp_data1 = resp.read()
verbose_print(verbosity, "Response on GET /hello : " + resp_data1)
# PUT message to /ctrl to enable /hello URI handler
verbose_print(verbosity, "Enabling /hello handler")
sess.send_put("/ctrl", "1")
sess.read_resp_hdrs()
if sess.content_len:
sess.read_resp_data()
sess.request("PUT", url="/ctrl", body="1")
resp = sess.getresponse()
resp.read()
sess.send_get("/hello")
sess.read_resp_hdrs()
resp_data2 = sess.read_resp_data()
sess.request("GET", url="/hello")
resp = sess.getresponse()
resp_data2 = resp.read()
verbose_print(verbosity, "Response on GET /hello : " + resp_data2)
# Close HTTP connection
@@ -222,14 +113,14 @@ def test_custom_uri_query(ip, port, query, verbosity = False):
verbose_print(verbosity, "======== GET HANDLER TEST =============")
# Establish HTTP connection
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
sess = Session(ip, port)
sess = httplib.HTTPConnection(ip + ":" + port)
uri = "/hello?" + query
# GET hello response
verbose_print(verbosity, "Sending GET to URI : ", uri)
sess.send_get(uri, {})
resp_hdrs = sess.read_resp_hdrs()
resp_data = sess.read_resp_data()
sess.request("GET", url=uri, headers={})
resp = sess.getresponse()
resp_data = resp.read()
verbose_print(verbosity, "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
verbose_print(verbosity, "Server response to GET /hello")
@@ -253,6 +144,9 @@ if __name__ == '__main__':
port = args['port']
msg = args['msg']
test_get_handler (ip, port, True)
test_post_handler(ip, port, msg, True)
test_put_handler (ip, port, True)
if not test_get_handler (ip, port, True):
print "Failed!"
if not test_post_handler(ip, port, msg, True):
print "Failed!"
if not test_put_handler (ip, port, True):
print "Failed!"