You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2021/12/14 11:26:53 UTC
svn commit: r1895945 [4/4] - in /httpd/httpd/branches/2.4.x: ./ test/ test/modules/http2/ test/modules/http2/conf/ test/modules/http2/data/ test/modules/http2/htdocs/ test/modules/http2/htdocs/cgi/ test/modules/http2/htdocs/test1/ test/modules/http2/ht...
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/forbidden.html
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/forbidden.html?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/forbidden.html (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/forbidden.html Tue Dec 14 11:26:52 2021
@@ -0,0 +1,11 @@
+<html>
+ <head>
+ <title>403 - Forbidden</title>
+ </head>
+ <body>
+ <h1>403 - Forbidden</h1>
+ <p>
+ An example of an error document.
+ </p>
+ </body>
+</html>
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/index.html
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/index.html?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/index.html (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/index.html Tue Dec 14 11:26:52 2021
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <title>mod_h2 test site generic</title>
+ </head>
+ <body>
+ <h1>mod_h2 test site generic</h1>
+ </body>
+</html>
+
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/001.html
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/001.html?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/001.html (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/001.html Tue Dec 14 11:26:52 2021
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+ <html>
+ <head>
+ <title>HTML/2.0 Test File: 001</title>
+ </head>
+ <body>
+ <p><h1>HTML/2.0 Test File: 001</h1></p>
+ <p>This file only contains a simple HTML structure with plain text.</p>
+ </body>
+</html>
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/002.jpg
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/002.jpg?rev=1895945&view=auto
==============================================================================
Binary file - no diff available.
Propchange: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/002.jpg
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/003.html
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/003.html?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/003.html (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/003.html Tue Dec 14 11:26:52 2021
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+ <html>
+ <head>
+ <title>HTML/2.0 Test File: 003</title>
+ </head>
+ <body>
+ <p><h1>HTML/2.0 Test File: 003</h1></p>
+ <p>This is a text HTML file with a big image:</p>
+ <p><img src="003/003_img.jpg" alt="GSMA Logo" style="width:269px;height:249px"></p>
+ </body>
+</html>
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/003/003_img.jpg
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/003/003_img.jpg?rev=1895945&view=auto
==============================================================================
Binary file - no diff available.
Propchange: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/003/003_img.jpg
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/004.html
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/004.html?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/004.html (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/004.html Tue Dec 14 11:26:52 2021
@@ -0,0 +1,23 @@
+<html>
+ <head>
+ <title>HTML/2.0 Test File: 004</title>
+ </head>
+ <body>
+ <p><h1>HTML/2.0 Test File: 004</h1>
+ This file contains plain text with a bunch of images.<br>
+ <img src="004/gophertiles_142.jpg" height="32" width="32"><img src="004/gophertiles_084.jpg" height="32" width="32"><img src="004/gophertiles_052.jpg" height="32" width="32"><img src="004/gophertiles_077.jpg" height="32" width="32"><img src="004/gophertiles_030.jpg" height="32" width="32"><img src="004/gophertiles_027.jpg" height="32" width="32"><img src="004/gophertiles_039.jpg" height="32" width="32"><img src="004/gophertiles_025.jpg" height="32" width="32"><img src="004/gophertiles_017.jpg" height="32" width="32"><img src="004/gophertiles_179.jpg" height="32" width="32"><img src="004/gophertiles_032.jpg" height="32" width="32"><img src="004/gophertiles_161.jpg" height="32" width="32"><img src="004/gophertiles_088.jpg" height="32" width="32"><img src="004/gophertiles_022.jpg" height="32" width="32"><img src="004/gophertiles_146.jpg" height="32" width="32"><br>
+ <img src="004/gophertiles_102.jpg" height="32" width="32"><img src="004/gophertiles_009.jpg" height="32" width="32"><img src="004/gophertiles_132.jpg" height="32" width="32"><img src="004/gophertiles_137.jpg" height="32" width="32"><img src="004/gophertiles_055.jpg" height="32" width="32"><img src="004/gophertiles_036.jpg" height="32" width="32"><img src="004/gophertiles_127.jpg" height="32" width="32"><img src="004/gophertiles_145.jpg" height="32" width="32"><img src="004/gophertiles_147.jpg" height="32" width="32"><img src="004/gophertiles_153.jpg" height="32" width="32"><img src="004/gophertiles_105.jpg" height="32" width="32"><img src="004/gophertiles_103.jpg" height="32" width="32"><img src="004/gophertiles_033.jpg" height="32" width="32"><img src="004/gophertiles_054.jpg" height="32" width="32"><img src="004/gophertiles_015.jpg" height="32" width="32"><br>
+ <img src="004/gophertiles_016.jpg" height="32" width="32"><img src="004/gophertiles_072.jpg" height="32" width="32"><img src="004/gophertiles_115.jpg" height="32" width="32"><img src="004/gophertiles_108.jpg" height="32" width="32"><img src="004/gophertiles_148.jpg" height="32" width="32"><img src="004/gophertiles_070.jpg" height="32" width="32"><img src="004/gophertiles_083.jpg" height="32" width="32"><img src="004/gophertiles_118.jpg" height="32" width="32"><img src="004/gophertiles_053.jpg" height="32" width="32"><img src="004/gophertiles_021.jpg" height="32" width="32"><img src="004/gophertiles_059.jpg" height="32" width="32"><img src="004/gophertiles_130.jpg" height="32" width="32"><img src="004/gophertiles_163.jpg" height="32" width="32"><img src="004/gophertiles_098.jpg" height="32" width="32"><img src="004/gophertiles_064.jpg" height="32" width="32"><br>
+ <img src="004/gophertiles_018.jpg" height="32" width="32"><img src="004/gophertiles_058.jpg" height="32" width="32"><img src="004/gophertiles_167.jpg" height="32" width="32"><img src="004/gophertiles_082.jpg" height="32" width="32"><img src="004/gophertiles_056.jpg" height="32" width="32"><img src="004/gophertiles_180.jpg" height="32" width="32"><img src="004/gophertiles_046.jpg" height="32" width="32"><img src="004/gophertiles_093.jpg" height="32" width="32"><img src="004/gophertiles_106.jpg" height="32" width="32"><img src="004/gophertiles_065.jpg" height="32" width="32"><img src="004/gophertiles_175.jpg" height="32" width="32"><img src="004/gophertiles_139.jpg" height="32" width="32"><img src="004/gophertiles_101.jpg" height="32" width="32"><img src="004/gophertiles_099.jpg" height="32" width="32"><img src="004/gophertiles_051.jpg" height="32" width="32"><br>
+ <img src="004/gophertiles_140.jpg" height="32" width="32"><img src="004/gophertiles_134.jpg" height="32" width="32"><img src="004/gophertiles_149.jpg" height="32" width="32"><img src="004/gophertiles_049.jpg" height="32" width="32"><img src="004/gophertiles_095.jpg" height="32" width="32"><img src="004/gophertiles_075.jpg" height="32" width="32"><img src="004/gophertiles_066.jpg" height="32" width="32"><img src="004/gophertiles_090.jpg" height="32" width="32"><img src="004/gophertiles_035.jpg" height="32" width="32"><img src="004/gophertiles_114.jpg" height="32" width="32"><img src="004/gophertiles_160.jpg" height="32" width="32"><img src="004/gophertiles_079.jpg" height="32" width="32"><img src="004/gophertiles_062.jpg" height="32" width="32"><img src="004/gophertiles_096.jpg" height="32" width="32"><img src="004/gophertiles_100.jpg" height="32" width="32"><br>
+ <img src="004/gophertiles_104.jpg" height="32" width="32"><img src="004/gophertiles_057.jpg" height="32" width="32"><img src="004/gophertiles_037.jpg" height="32" width="32"><img src="004/gophertiles_086.jpg" height="32" width="32"><img src="004/gophertiles_168.jpg" height="32" width="32"><img src="004/gophertiles_138.jpg" height="32" width="32"><img src="004/gophertiles_045.jpg" height="32" width="32"><img src="004/gophertiles_141.jpg" height="32" width="32"><img src="004/gophertiles_029.jpg" height="32" width="32"><img src="004/gophertiles_165.jpg" height="32" width="32"><img src="004/gophertiles_110.jpg" height="32" width="32"><img src="004/gophertiles_063.jpg" height="32" width="32"><img src="004/gophertiles_158.jpg" height="32" width="32"><img src="004/gophertiles_122.jpg" height="32" width="32"><img src="004/gophertiles_068.jpg" height="32" width="32"><br>
+ <img src="004/gophertiles_170.jpg" height="32" width="32"><img src="004/gophertiles_120.jpg" height="32" width="32"><img src="004/gophertiles_117.jpg" height="32" width="32"><img src="004/gophertiles_031.jpg" height="32" width="32"><img src="004/gophertiles_113.jpg" height="32" width="32"><img src="004/gophertiles_074.jpg" height="32" width="32"><img src="004/gophertiles_129.jpg" height="32" width="32"><img src="004/gophertiles_019.jpg" height="32" width="32"><img src="004/gophertiles_060.jpg" height="32" width="32"><img src="004/gophertiles_109.jpg" height="32" width="32"><img src="004/gophertiles_080.jpg" height="32" width="32"><img src="004/gophertiles_097.jpg" height="32" width="32"><img src="004/gophertiles_116.jpg" height="32" width="32"><img src="004/gophertiles_085.jpg" height="32" width="32"><img src="004/gophertiles_050.jpg" height="32" width="32"><br>
+ <img src="004/gophertiles_151.jpg" height="32" width="32"><img src="004/gophertiles_094.jpg" height="32" width="32"><img src="004/gophertiles_067.jpg" height="32" width="32"><img src="004/gophertiles_128.jpg" height="32" width="32"><img src="004/gophertiles_034.jpg" height="32" width="32"><img src="004/gophertiles_135.jpg" height="32" width="32"><img src="004/gophertiles_012.jpg" height="32" width="32"><img src="004/gophertiles_010.jpg" height="32" width="32"><img src="004/gophertiles_152.jpg" height="32" width="32"><img src="004/gophertiles_171.jpg" height="32" width="32"><img src="004/gophertiles_087.jpg" height="32" width="32"><img src="004/gophertiles_126.jpg" height="32" width="32"><img src="004/gophertiles_048.jpg" height="32" width="32"><img src="004/gophertiles_023.jpg" height="32" width="32"><img src="004/gophertiles_078.jpg" height="32" width="32"><br>
+ <img src="004/gophertiles_071.jpg" height="32" width="32"><img src="004/gophertiles_131.jpg" height="32" width="32"><img src="004/gophertiles_073.jpg" height="32" width="32"><img src="004/gophertiles_143.jpg" height="32" width="32"><img src="004/gophertiles_173.jpg" height="32" width="32"><img src="004/gophertiles_154.jpg" height="32" width="32"><img src="004/gophertiles_061.jpg" height="32" width="32"><img src="004/gophertiles_178.jpg" height="32" width="32"><img src="004/gophertiles_013.jpg" height="32" width="32"><img src="004/gophertiles_028.jpg" height="32" width="32"><img src="004/gophertiles_157.jpg" height="32" width="32"><img src="004/gophertiles_038.jpg" height="32" width="32"><img src="004/gophertiles_069.jpg" height="32" width="32"><img src="004/gophertiles_174.jpg" height="32" width="32"><img src="004/gophertiles_076.jpg" height="32" width="32"><br>
+ <img src="004/gophertiles_155.jpg" height="32" width="32"><img src="004/gophertiles_107.jpg" height="32" width="32"><img src="004/gophertiles_136.jpg" height="32" width="32"><img src="004/gophertiles_144.jpg" height="32" width="32"><img src="004/gophertiles_091.jpg" height="32" width="32"><img src="004/gophertiles_024.jpg" height="32" width="32"><img src="004/gophertiles_014.jpg" height="32" width="32"><img src="004/gophertiles_159.jpg" height="32" width="32"><img src="004/gophertiles_011.jpg" height="32" width="32"><img src="004/gophertiles_176.jpg" height="32" width="32"><img src="004/gophertiles_162.jpg" height="32" width="32"><img src="004/gophertiles_156.jpg" height="32" width="32"><img src="004/gophertiles_081.jpg" height="32" width="32"><img src="004/gophertiles_119.jpg" height="32" width="32"><img src="004/gophertiles_026.jpg" height="32" width="32"><br>
+ <img src="004/gophertiles_133.jpg" height="32" width="32"><img src="004/gophertiles_020.jpg" height="32" width="32"><img src="004/gophertiles_044.jpg" height="32" width="32"><img src="004/gophertiles_125.jpg" height="32" width="32"><img src="004/gophertiles_150.jpg" height="32" width="32"><img src="004/gophertiles_172.jpg" height="32" width="32"><img src="004/gophertiles_002.jpg" height="32" width="32"><img src="004/gophertiles_169.jpg" height="32" width="32"><img src="004/gophertiles_007.jpg" height="32" width="32"><img src="004/gophertiles_008.jpg" height="32" width="32"><img src="004/gophertiles_042.jpg" height="32" width="32"><img src="004/gophertiles_041.jpg" height="32" width="32"><img src="004/gophertiles_166.jpg" height="32" width="32"><img src="004/gophertiles_005.jpg" height="32" width="32"><img src="004/gophertiles_089.jpg" height="32" width="32"><br>
+ <img src="004/gophertiles_177.jpg" height="32" width="32"><img src="004/gophertiles_092.jpg" height="32" width="32"><img src="004/gophertiles_043.jpg" height="32" width="32"><img src="004/gophertiles_111.jpg" height="32" width="32"><img src="004/gophertiles_047.jpg" height="32" width="32"><img src="004/gophertiles.jpg" height="32" width="32"><img src="004/gophertiles_006.jpg" height="32" width="32"><img src="004/gophertiles_121.jpg" height="32" width="32"><img src="004/gophertiles_004.jpg" height="32" width="32"><img src="004/gophertiles_124.jpg" height="32" width="32"><img src="004/gophertiles_123.jpg" height="32" width="32"><img src="004/gophertiles_112.jpg" height="32" width="32"><img src="004/gophertiles_040.jpg" height="32" width="32"><img src="004/gophertiles_164.jpg" height="32" width="32"><img src="004/gophertiles_003.jpg" height="32" width="32"><br>
+ <hr>This page is developed using this template:<a href="https://http2.golang.org/">HTTP/2 demo server</a>
+ </p>
+ </body>
+</html>
\ No newline at end of file
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/004/gophertiles.jpg
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/004/gophertiles.jpg?rev=1895945&view=auto
==============================================================================
Binary file - no diff available.
Propchange: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/004/gophertiles.jpg
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006.html
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006.html?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006.html (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006.html Tue Dec 14 11:26:52 2021
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+ <html>
+ <head>
+ <title>HTML/2.0 Test File: 006</title>
+ <link rel="stylesheet" type="text/css" href="006/006.css">
+ <script type="text/javascript" src="006/006.js"></script>
+ </head>
+ <body>
+ <h1>HTML/2.0 Test File: 006</h1>
+ <div class="listTitle">This page contains:
+ <ul class="listElements">
+ <li>HTML
+ <li>CSS
+ <li>JavaScript
+ </ul>
+ </div>
+ <div class="listTitle">
+ <script type="text/javascript">
+ mainJavascript();
+ </script>
+ </div>
+ </body>
+</html>
\ No newline at end of file
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006/006.css
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006/006.css?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006/006.css (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006/006.css Tue Dec 14 11:26:52 2021
@@ -0,0 +1,21 @@
+@CHARSET "ISO-8859-1";
+body{
+ background:HoneyDew;
+}
+p{
+color:#0000FF;
+text-align:left;
+}
+
+h1{
+color:#FF0000;
+text-align:center;
+}
+
+.listTitle{
+ font-size:large;
+}
+
+.listElements{
+ color:#3366FF
+}
\ No newline at end of file
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006/006.js
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006/006.js?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006/006.js (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006/006.js Tue Dec 14 11:26:52 2021
@@ -0,0 +1,31 @@
+/**
+ * JavaScript Functions File
+ */
+function returnDate()
+{
+ var currentDate;
+ currentDate=new Date();
+ var dateString=(currentDate.getMonth()+1)+'/'+currentDate.getDate()+'/'+currentDate.getFullYear();
+ return dateString;
+}
+
+function returnHour()
+{
+ var currentDate;
+ currentDate=new Date();
+ var hourString=currentDate.getHours()+':'+currentDate.getMinutes()+':'+currentDate.getSeconds();
+ return hourString;
+}
+
+function javaScriptMessage(){
+ return 'This section is generated under JavaScript:<br>';
+}
+
+function mainJavascript(){
+ document.write(javaScriptMessage())
+ document.write('<ul class="listElements">');
+ document.write('<li>Current date (dd/mm/yyyy): ' + returnDate());
+ document.write('<br>');
+ document.write('<li>Current time (hh:mm:ss): '+returnHour());
+ document.write('</ul>');
+}
\ No newline at end of file
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006/header.html
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006/header.html?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006/header.html (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/006/header.html Tue Dec 14 11:26:52 2021
@@ -0,0 +1 @@
+<title>My Header Title</title>
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/007.html
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/007.html?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/007.html (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/007.html Tue Dec 14 11:26:52 2021
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="ISO-8859-1">
+<title>HTML/2.0 Test File: 007</title>
+</head>
+<body>
+ <h1>HTML/2.0 Test File: 007</h1>
+ <div><p>This page is used to send data from the client to the server:</p>
+ <FORM ACTION="007/007.py" METHOD="post" ENCTYPE="multipart/form-data">
+ <input type="hidden" name="pageName" value="007.html">
+ Name:<input type="text" name="pName" value="Write your name here." size="30" maxlength="30"><br>
+ Age:<input type="text" name="pAge" value="00" size="2" maxlength="2"><br>
+ Gender: Male<input type="radio" name="pGender" VALUE="Male">
+ Female<input type="radio" name="pGender" VALUE="Female"><br>
+ <input type="submit" name="userForm" value="Send">
+ <input type="reset" value="Clear">
+ </FORM>
+ </div>
+</body>
+</html>
\ No newline at end of file
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/007/007.py
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/007/007.py?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/007/007.py (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/007/007.py Tue Dec 14 11:26:52 2021
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+import cgi, sys
+import cgitb; cgitb.enable()
+
+print "Content-Type: text/html;charset=UTF-8"
+print
+
+print """\
+ <!DOCTYPE html><html><head>
+ <title>HTML/2.0 Test File: 007 (received data)</title></head>
+ <body><h1>HTML/2.0 Test File: 007</h1>"""
+
+# alternative output: parsed form params <-> plain POST body
+parseContent = True # <-> False
+
+if parseContent:
+ print '<h2>Data processed:</h2><ul>'
+ form = cgi.FieldStorage()
+ for name in form:
+ print '<li>', name, ': ', form[name].value, '</li>'
+ print '</ul>'
+else:
+ print '<h2>POST data output:</h2><div><pre>'
+ data = sys.stdin.read()
+ print data
+ print '</pre></div>'
+
+print '</body></html>'
\ No newline at end of file
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/009.py
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/009.py?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/009.py (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/009.py Tue Dec 14 11:26:52 2021
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+import cgi, sys, time
+import cgitb; cgitb.enable()
+
+print "Content-Type: text/html;charset=UTF-8"
+print
+
+print """\
+ <!DOCTYPE html><html><head>
+ <title>HTML/2.0 Test File: 009 (server time)</title></head>
+ <body><h1>HTML/2.0 Test File: 009</h1>
+ <p>60 seconds of server time, one by one.</p>"""
+
+for i in range(60):
+ s = time.strftime("%Y-%m-%d %H:%M:%S")
+ print "<div>", s, "</div>"
+ sys.stdout.flush()
+ time.sleep(1)
+
+print "<p>done.</p></body></html>"
\ No newline at end of file
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/alive.json
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/alive.json?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/alive.json (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/alive.json Tue Dec 14 11:26:52 2021
@@ -0,0 +1,5 @@
+{
+ "host" : "test1",
+ "alive" : true
+}
+
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/index.html
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/index.html?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/index.html (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test1/index.html Tue Dec 14 11:26:52 2021
@@ -0,0 +1,46 @@
+<html>
+ <head>
+ <title>mod_h2 test site</title>
+ </head>
+ <body>
+ <h1>mod_h2 test site</h1>
+ <p></p>
+ <h2>served directly</h2>
+ <ul>
+ <li><a href="001.html">01: html</a></li>
+ <li><a href="002.jpg">02: image</a></li>
+ <li><a href="003.html">03: html+image</a></li>
+ <li><a href="004.html">04: tiled image</a></li>
+ <li><a href="005.txt">05: large text</a></li>
+ <li><a href="006.html">06: html/js/css</a></li>
+ <li><a href="007.html">07: form submit</a></li>
+ <li><a href="upload.py">08: upload</a></li>
+ <li><a href="009.py">09: small chunks</a></li>
+ </ul>
+ <h2>mod_proxyied</h2>
+ <ul>
+ <li><a href="proxy/001.html">01: html</a></li>
+ <li><a href="proxy/002.jpg">02: image</a></li>
+ <li><a href="proxy/003.html">03: html+image</a></li>
+ <li><a href="proxy/004.html">04: tiled image</a></li>
+ <li><a href="proxy/005.txt">05: large text</a></li>
+ <li><a href="proxy/006.html">06: html/js/css</a></li>
+ <li><a href="proxy/007.html">07: form submit</a></li>
+ <li><a href="proxy/upload.py">08: upload</a></li>
+ <li><a href="proxy/009.py">09: small chunks</a></li>
+ </ul>
+ <h2>mod_rewritten</h2>
+ <ul>
+ <li><a href="rewrite/001.html">01: html</a></li>
+ <li><a href="rewrite/002.jpg">02: image</a></li>
+ <li><a href="rewrite/003.html">03: html+image</a></li>
+ <li><a href="rewrite/004.html">04: tiled image</a></li>
+ <li><a href="rewrite/005.txt">05: large text</a></li>
+ <li><a href="rewrite/006.html">06: html/js/css</a></li>
+ <li><a href="rewrite/007.html">07: form submit</a></li>
+ <li><a href="rewrite/upload.py">08: upload</a></li>
+ <li><a href="rewrite/009.py">09: small chunks</a></li>
+ </ul>
+ </body>
+</html>
+
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/006/006.css
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/006/006.css?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/006/006.css (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/006/006.css Tue Dec 14 11:26:52 2021
@@ -0,0 +1,21 @@
+@CHARSET "ISO-8859-1";
+body{
+ background:HoneyDew;
+}
+p{
+color:#0000FF;
+text-align:left;
+}
+
+h1{
+color:#FF0000;
+text-align:center;
+}
+
+.listTitle{
+ font-size:large;
+}
+
+.listElements{
+ color:#3366FF
+}
\ No newline at end of file
Propchange: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/006/006.css
------------------------------------------------------------------------------
svn:executable = *
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/10%abnormal.txt
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/10%25abnormal.txt?rev=1895945&view=auto
==============================================================================
(empty)
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/alive.json
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/alive.json?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/alive.json (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/alive.json Tue Dec 14 11:26:52 2021
@@ -0,0 +1,4 @@
+{
+ "host" : "test2",
+ "alive" : true
+}
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/x%2f.test
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/htdocs/test2/x%252f.test?rev=1895945&view=auto
==============================================================================
(empty)
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/log.py
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/log.py?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/log.py (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/log.py Tue Dec 14 11:26:52 2021
@@ -0,0 +1,163 @@
+import os
+import re
+import time
+from datetime import datetime, timedelta
+from io import SEEK_END
+from typing import List, Tuple, Any
+
+
+class HttpdErrorLog:
+ """Checking the httpd error log for errors and warnings, including
+ limiting checks from a last known position forward.
+ """
+
+ RE_ERRLOG_ERROR = re.compile(r'.*\[(?P<module>[^:]+):error].*')
+ RE_ERRLOG_WARN = re.compile(r'.*\[(?P<module>[^:]+):warn].*')
+ RE_APLOGNO = re.compile(r'.*\[(?P<module>[^:]+):(error|warn)].* (?P<aplogno>AH\d+): .+')
+ RE_SSL_LIB_ERR = re.compile(r'.*\[ssl:error].* SSL Library Error: error:(?P<errno>\S+):.+')
+
+ def __init__(self, path: str):
+ self._path = path
+ self._ignored_modules = []
+ self._ignored_lognos = set()
+ self._ignored_patterns = []
+ # remember the file position we started with
+ self._start_pos = 0
+ if os.path.isfile(self._path):
+ with open(self._path) as fd:
+ self._start_pos = fd.seek(0, SEEK_END)
+ self._last_pos = self._start_pos
+ self._last_errors = []
+ self._last_warnings = []
+ self._observed_erros = set()
+ self._observed_warnings = set()
+
+ def __repr__(self):
+ return f"HttpdErrorLog[{self._path}, errors: {' '.join(self._last_errors)}, " \
+ f"warnings: {' '.join(self._last_warnings)}]"
+
+ @property
+ def path(self) -> str:
+ return self._path
+
+ def clear_log(self):
+ if os.path.isfile(self.path):
+ os.remove(self.path)
+ self._start_pos = 0
+ self._last_pos = self._start_pos
+ self._last_errors = []
+ self._last_warnings = []
+ self._observed_erros = set()
+ self._observed_warnings = set()
+
+ def set_ignored_modules(self, modules: List[str]):
+ self._ignored_modules = modules.copy() if modules else []
+
+ def set_ignored_lognos(self, lognos: List[str]):
+ if lognos:
+ for l in lognos:
+ self._ignored_lognos.add(l)
+
+ def add_ignored_patterns(self, patterns: List[Any]):
+ self._ignored_patterns.extend(patterns)
+
+ def _is_ignored(self, line: str) -> bool:
+ for p in self._ignored_patterns:
+ if p.match(line):
+ return True
+ m = self.RE_APLOGNO.match(line)
+ if m and m.group('aplogno') in self._ignored_lognos:
+ return True
+ return False
+
+ def get_recent(self, advance=True) -> Tuple[List[str], List[str]]:
+ """Collect error and warning from the log since the last remembered position
+ :param advance: advance the position to the end of the log afterwards
+ :return: list of error and list of warnings as tuple
+ """
+ self._last_errors = []
+ self._last_warnings = []
+ if os.path.isfile(self._path):
+ with open(self._path) as fd:
+ fd.seek(self._last_pos, os.SEEK_SET)
+ for line in fd:
+ if self._is_ignored(line):
+ continue
+ m = self.RE_ERRLOG_ERROR.match(line)
+ if m and m.group('module') not in self._ignored_modules:
+ self._last_errors.append(line)
+ continue
+ m = self.RE_ERRLOG_WARN.match(line)
+ if m:
+ if m and m.group('module') not in self._ignored_modules:
+ self._last_warnings.append(line)
+ continue
+ if advance:
+ self._last_pos = fd.tell()
+ self._observed_erros.update(set(self._last_errors))
+ self._observed_warnings.update(set(self._last_warnings))
+ return self._last_errors, self._last_warnings
+
+ def get_recent_count(self, advance=True):
+ errors, warnings = self.get_recent(advance=advance)
+ return len(errors), len(warnings)
+
+ def ignore_recent(self):
+ """After a test case triggered errors/warnings on purpose, add
+ those to our 'observed' list so the do not get reported as 'missed'.
+ """
+ self._last_errors = []
+ self._last_warnings = []
+ if os.path.isfile(self._path):
+ with open(self._path) as fd:
+ fd.seek(self._last_pos, os.SEEK_SET)
+ for line in fd:
+ if self._is_ignored(line):
+ continue
+ m = self.RE_ERRLOG_ERROR.match(line)
+ if m and m.group('module') not in self._ignored_modules:
+ self._observed_erros.add(line)
+ continue
+ m = self.RE_ERRLOG_WARN.match(line)
+ if m:
+ if m and m.group('module') not in self._ignored_modules:
+ self._observed_warnings.add(line)
+ continue
+ self._last_pos = fd.tell()
+
+ def get_missed(self) -> Tuple[List[str], List[str]]:
+ errors = []
+ warnings = []
+ if os.path.isfile(self._path):
+ with open(self._path) as fd:
+ fd.seek(self._start_pos, os.SEEK_SET)
+ for line in fd:
+ if self._is_ignored(line):
+ continue
+ m = self.RE_ERRLOG_ERROR.match(line)
+ if m and m.group('module') not in self._ignored_modules \
+ and line not in self._observed_erros:
+ errors.append(line)
+ continue
+ m = self.RE_ERRLOG_WARN.match(line)
+ if m:
+ if m and m.group('module') not in self._ignored_modules \
+ and line not in self._observed_warnings:
+ warnings.append(line)
+ continue
+ return errors, warnings
+
+ def scan_recent(self, pattern: re, timeout=10):
+ if not os.path.isfile(self.path):
+ return False
+ with open(self.path) as fd:
+ end = datetime.now() + timedelta(seconds=timeout)
+ while True:
+ fd.seek(self._last_pos, os.SEEK_SET)
+ for line in fd:
+ if pattern.match(line):
+ return True
+ if datetime.now() > end:
+ raise TimeoutError(f"pattern not found in error log after {timeout} seconds")
+ time.sleep(.1)
+ return False
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/nghttp.py
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/nghttp.py?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/nghttp.py (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/nghttp.py Tue Dec 14 11:26:52 2021
@@ -0,0 +1,289 @@
+import re
+import os
+import subprocess
+from datetime import datetime
+from typing import Dict
+
+from urllib.parse import urlparse
+
+from .result import ExecResult
+
+
+def _get_path(x):
+ return x["path"]
+
+
+class Nghttp:
+
+ def __init__(self, path, connect_addr=None, tmp_dir="/tmp"):
+ self.NGHTTP = path
+ self.CONNECT_ADDR = connect_addr
+ self.TMP_DIR = tmp_dir
+
+ @staticmethod
+ def get_stream(streams, sid):
+ sid = int(sid)
+ if sid not in streams:
+ streams[sid] = {
+ "id": sid,
+ "header": {},
+ "request": {
+ "id": sid,
+ "body": b''
+ },
+ "response": {
+ "id": sid,
+ "body": b''
+ },
+ "paddings": [],
+ "promises": []
+ }
+ return streams[sid] if sid in streams else None
+
+ def run(self, urls, timeout, options):
+ return self._baserun(urls, timeout, options)
+
+ def complete_args(self, url, _timeout, options: [str]) -> [str]:
+ if not isinstance(url, list):
+ url = [url]
+ u = urlparse(url[0])
+ args = [self.NGHTTP]
+ if self.CONNECT_ADDR:
+ connect_host = self.CONNECT_ADDR
+ args.append("--header=host: %s:%s" % (u.hostname, u.port))
+ else:
+ connect_host = u.hostname
+ if options:
+ args.extend(options)
+ for xurl in url:
+ xu = urlparse(xurl)
+ nurl = "%s://%s:%s/%s" % (u.scheme, connect_host, xu.port, xu.path)
+ if xu.query:
+ nurl = "%s?%s" % (nurl, xu.query)
+ args.append(nurl)
+ return args
+
+ def _baserun(self, url, timeout, options):
+ return self._run(self.complete_args(url, timeout, options))
+
+ def parse_output(self, btext) -> Dict:
+ # getting meta data and response body out of nghttp's output
+ # is a bit tricky. Without '-v' we just get the body. With '-v' meta
+ # data and timings in both directions are listed.
+ # We rely on response :status: to be unique and on
+ # response body not starting with space.
+ # Something not good enough for general purpose, but for these tests.
+ output = {}
+ body = ''
+ streams = {}
+ skip_indents = True
+ # chunk output into lines. nghttp mixes text
+ # meta output with bytes from the response body.
+ lines = [l.decode() for l in btext.split(b'\n')]
+ for lidx, l in enumerate(lines):
+ if len(l) == 0:
+ body += '\n'
+ continue
+ m = re.match(r'\[.*] recv \(stream_id=(\d+)\) (\S+): (\S*)', l)
+ if m:
+ s = self.get_stream(streams, m.group(1))
+ hname = m.group(2)
+ hval = m.group(3)
+ print("stream %d header %s: %s" % (s["id"], hname, hval))
+ header = s["header"]
+ if hname in header:
+ header[hname] += ", %s" % hval
+ else:
+ header[hname] = hval
+ body = ''
+ continue
+
+ m = re.match(r'\[.*] recv HEADERS frame <.* stream_id=(\d+)>', l)
+ if m:
+ s = self.get_stream(streams, m.group(1))
+ if s:
+ print("stream %d: recv %d header" % (s["id"], len(s["header"])))
+ response = s["response"]
+ hkey = "header"
+ if "header" in response:
+ h = response["header"]
+ if ":status" in h and int(h[":status"]) >= 200:
+ hkey = "trailer"
+ else:
+ prev = {
+ "header": h
+ }
+ if "previous" in response:
+ prev["previous"] = response["previous"]
+ response["previous"] = prev
+ response[hkey] = s["header"]
+ s["header"] = {}
+ body = ''
+ continue
+
+ m = re.match(r'(.*)\[.*] recv DATA frame <length=(\d+), .*stream_id=(\d+)>', l)
+ if m:
+ s = self.get_stream(streams, m.group(3))
+ body += m.group(1)
+ blen = int(m.group(2))
+ if s:
+ print("stream %d: %d DATA bytes added" % (s["id"], blen))
+ padlen = 0
+ if len(lines) > lidx + 2:
+ mpad = re.match(r' +\(padlen=(\d+)\)', lines[lidx+2])
+ if mpad:
+ padlen = int(mpad.group(1))
+ s["paddings"].append(padlen)
+ blen -= padlen
+ s["response"]["body"] += body[-blen:].encode()
+ body = ''
+ skip_indents = True
+ continue
+
+ m = re.match(r'\[.*] recv PUSH_PROMISE frame <.* stream_id=(\d+)>', l)
+ if m:
+ s = self.get_stream(streams, m.group(1))
+ if s:
+ # headers we have are request headers for the PUSHed stream
+ # these have been received on the originating stream, the promised
+ # stream id it mentioned in the following lines
+ print("stream %d: %d PUSH_PROMISE header" % (s["id"], len(s["header"])))
+ if len(lines) > lidx+2:
+ m2 = re.match(r'\s+\(.*promised_stream_id=(\d+)\)', lines[lidx+2])
+ if m2:
+ s2 = self.get_stream(streams, m2.group(1))
+ s2["request"]["header"] = s["header"]
+ s["promises"].append(s2)
+ s["header"] = {}
+ continue
+
+ m = re.match(r'(.*)\[.*] recv (\S+) frame <length=(\d+), .*stream_id=(\d+)>', l)
+ if m:
+ print("recv frame %s on stream %s" % (m.group(2), m.group(4)))
+ body += m.group(1)
+ skip_indents = True
+ continue
+
+ m = re.match(r'(.*)\[.*] send (\S+) frame <length=(\d+), .*stream_id=(\d+)>', l)
+ if m:
+ print("send frame %s on stream %s" % (m.group(2), m.group(4)))
+ body += m.group(1)
+ skip_indents = True
+ continue
+
+ if skip_indents and l.startswith(' '):
+ continue
+
+ if '[' != l[0]:
+ skip_indents = None
+ body += l + '\n'
+
+ # the main request is done on the lowest odd numbered id
+ main_stream = 99999999999
+ for sid in streams:
+ s = streams[sid]
+ if "header" in s["response"] and ":status" in s["response"]["header"]:
+ s["response"]["status"] = int(s["response"]["header"][":status"])
+ if (sid % 2) == 1 and sid < main_stream:
+ main_stream = sid
+
+ output["streams"] = streams
+ if main_stream in streams:
+ output["response"] = streams[main_stream]["response"]
+ output["paddings"] = streams[main_stream]["paddings"]
+ return output
+
+ def _raw(self, url, timeout, options):
+ args = ["-v"]
+ if options:
+ args.extend(options)
+ r = self._baserun(url, timeout, args)
+ if 0 == r.exit_code:
+ r.add_results(self.parse_output(r.outraw))
+ return r
+
+ def get(self, url, timeout=5, options=None):
+ return self._raw(url, timeout, options)
+
+ def assets(self, url, timeout=5, options=None):
+ if not options:
+ options = []
+ options.extend(["-ans"])
+ r = self._baserun(url, timeout, options)
+ assets = []
+ if 0 == r.exit_code:
+ lines = re.findall(r'[^\n]*\n', r.stdout, re.MULTILINE)
+ for lidx, l in enumerate(lines):
+ m = re.match(r'\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+/(.*)', l)
+ if m:
+ assets.append({
+ "path": m.group(7),
+ "status": int(m.group(5)),
+ "size": m.group(6)
+ })
+ assets.sort(key=_get_path)
+ r.add_assets(assets)
+ return r
+
+ def post_data(self, url, data, timeout=5, options=None):
+ reqbody = ("%s/nghttp.req.body" % self.TMP_DIR)
+ with open(reqbody, 'wb') as f:
+ f.write(data.encode('utf-8'))
+ if not options:
+ options = []
+ options.extend(["--data=%s" % reqbody])
+ return self._raw(url, timeout, options)
+
+ def post_name(self, url, name, timeout=5, options=None):
+ reqbody = ("%s/nghttp.req.body" % self.TMP_DIR)
+ with open(reqbody, 'w') as f:
+ f.write("--DSAJKcd9876\n")
+ f.write("Content-Disposition: form-data; name=\"value\"; filename=\"xxxxx\"\n")
+ f.write("Content-Type: text/plain\n")
+ f.write("\n%s\n" % name)
+ f.write("--DSAJKcd9876\n")
+ if not options:
+ options = []
+ options.extend(["--data=%s" % reqbody])
+ return self._raw(url, timeout, options)
+
+ def upload(self, url, fpath, timeout=5, options=None):
+ if not options:
+ options = []
+ options.extend(["--data=%s" % fpath])
+ return self._raw(url, timeout, options)
+
+ def upload_file(self, url, fpath, timeout=5, options=None):
+ fname = os.path.basename(fpath)
+ reqbody = ("%s/nghttp.req.body" % self.TMP_DIR)
+ with open(fpath, 'rb') as fin:
+ with open(reqbody, 'wb') as f:
+ f.write(("""--DSAJKcd9876
+Content-Disposition: form-data; name="xxx"; filename="xxxxx"
+Content-Type: text/plain
+
+testing mod_h2
+--DSAJKcd9876
+Content-Disposition: form-data; name="file"; filename="%s"
+Content-Type: application/octet-stream
+Content-Transfer-Encoding: binary
+
+""" % fname).encode('utf-8'))
+ f.write(fin.read())
+ f.write("""
+--DSAJKcd9876""".encode('utf-8'))
+ if not options:
+ options = []
+ options.extend([
+ "--data=%s" % reqbody,
+ "--expect-continue",
+ "-HContent-Type: multipart/form-data; boundary=DSAJKcd9876"])
+ return self._raw(url, timeout, options)
+
+ def _run(self, args) -> ExecResult:
+ print(("execute: %s" % " ".join(args)))
+ start = datetime.now()
+ p = subprocess.run(args, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
+ return ExecResult(args=args, exit_code=p.returncode,
+ stdout=p.stdout, stderr=p.stderr,
+ duration=datetime.now() - start)
Added: httpd/httpd/branches/2.4.x/test/pyhttpd/result.py
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/test/pyhttpd/result.py?rev=1895945&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/test/pyhttpd/result.py (added)
+++ httpd/httpd/branches/2.4.x/test/pyhttpd/result.py Tue Dec 14 11:26:52 2021
@@ -0,0 +1,80 @@
+import json
+from datetime import timedelta
+from typing import Optional, Dict, List
+
+
+class ExecResult:
+
+ def __init__(self, args: List[str], exit_code: int,
+ stdout: bytes, stderr: bytes = None, duration: timedelta = None):
+ self._args = args
+ self._exit_code = exit_code
+ self._raw = stdout if stdout else b''
+ self._stdout = stdout.decode() if stdout is not None else ""
+ self._stderr = stderr.decode() if stderr is not None else ""
+ self._duration = duration if duration is not None else timedelta()
+ self._response = None
+ self._results = {}
+ self._assets = []
+ # noinspection PyBroadException
+ try:
+ self._json_out = json.loads(self._stdout)
+ except:
+ self._json_out = None
+
+ def __repr__(self):
+ return f"ExecResult[code={self.exit_code}, args={self._args}, stdout={self.stdout}, stderr={self.stderr}]"
+
+ @property
+ def exit_code(self) -> int:
+ return self._exit_code
+
+ @property
+ def args(self) -> List[str]:
+ return self._args
+
+ @property
+ def outraw(self) -> bytes:
+ return self._raw
+
+ @property
+ def stdout(self) -> str:
+ return self._stdout
+
+ @property
+ def json(self) -> Optional[Dict]:
+ """Output as JSON dictionary or None if not parseable."""
+ return self._json_out
+
+ @property
+ def stderr(self) -> str:
+ return self._stderr
+
+ @property
+ def duration(self) -> timedelta:
+ return self._duration
+
+ @property
+ def response(self) -> Optional[Dict]:
+ return self._response
+
+ @property
+ def results(self) -> Dict:
+ return self._results
+
+ @property
+ def assets(self) -> List:
+ return self._assets
+
+ def add_response(self, resp: Dict):
+ if self._response:
+ resp['previous'] = self._response
+ self._response = resp
+
+ def add_results(self, results: Dict):
+ self._results.update(results)
+ if 'response' in results:
+ self.add_response(results['response'])
+
+ def add_assets(self, assets: List):
+ self._assets.extend(assets)