summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/go/404/app.go22
-rw-r--r--test/go/command_line_arguments/app.go24
-rw-r--r--test/go/cookies/app.go16
-rw-r--r--test/go/empty/app.go8
-rw-r--r--test/go/get_variables/app.go14
-rw-r--r--test/go/mirror/app.go20
-rw-r--r--test/go/post_variables/app.go16
-rw-r--r--test/go/variables/app.go36
-rw-r--r--test/java/empty_war/empty.warbin0 -> 484 bytes
-rw-r--r--test/java/multipart/app.java93
-rw-r--r--test/java/session_inactive/app.java8
-rwxr-xr-xtest/node/404/app.js3
-rwxr-xr-xtest/node/basic/app.js4
-rwxr-xr-xtest/node/double_end/app.js3
-rwxr-xr-xtest/node/mirror/app.js4
-rwxr-xr-xtest/node/promise_handler/app.js3
-rwxr-xr-xtest/node/status_message/app.js3
-rwxr-xr-xtest/node/variables/app.js3
-rwxr-xr-xtest/node/websockets/mirror/app.js31
-rwxr-xr-xtest/node/websockets/mirror_fragmentation/app.js26
-rwxr-xr-xtest/node/write_before_write_head/app.js3
-rwxr-xr-xtest/node/write_buffer/app.js4
-rwxr-xr-xtest/node/write_return/app.js4
-rw-r--r--test/php/header/index.php4
-rw-r--r--test/php/script/phpinfo.php3
-rw-r--r--test/php/variables/index.php1
-rw-r--r--test/test_access_log.py12
-rw-r--r--test/test_java_application.py95
-rw-r--r--test/test_node_websockets.py1585
-rw-r--r--test/test_php_application.py83
-rw-r--r--test/test_php_basic.py26
-rw-r--r--test/test_python_basic.py27
-rw-r--r--test/test_routing.py1198
-rw-r--r--test/test_routing_tls.py58
-rw-r--r--test/test_tls.py5
-rw-r--r--test/unit/applications/lang/java.py2
-rw-r--r--test/unit/applications/lang/perl.py4
-rw-r--r--test/unit/applications/lang/php.py4
-rw-r--r--test/unit/applications/lang/python.py4
-rw-r--r--test/unit/applications/lang/ruby.py4
-rw-r--r--test/unit/applications/tls.py19
-rw-r--r--test/unit/applications/websockets.py215
-rw-r--r--test/unit/http.py12
-rw-r--r--test/unit/main.py35
44 files changed, 2946 insertions, 798 deletions
diff --git a/test/go/404/app.go b/test/go/404/app.go
index abb33066..08fe56c9 100644
--- a/test/go/404/app.go
+++ b/test/go/404/app.go
@@ -1,22 +1,22 @@
package main
import (
- "io"
- "io/ioutil"
- "net/http"
- "nginx/unit"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "nginx/unit"
)
func handler(w http.ResponseWriter, r *http.Request) {
- b, e := ioutil.ReadFile("404.html")
+ b, e := ioutil.ReadFile("404.html")
- if e == nil {
- w.WriteHeader(http.StatusNotFound)
- io.WriteString(w, string(b))
- }
+ if e == nil {
+ w.WriteHeader(http.StatusNotFound)
+ io.WriteString(w, string(b))
+ }
}
func main() {
- http.HandleFunc("/", handler)
- unit.ListenAndServe(":7080", nil)
+ http.HandleFunc("/", handler)
+ unit.ListenAndServe(":7080", nil)
}
diff --git a/test/go/command_line_arguments/app.go b/test/go/command_line_arguments/app.go
index 228e07c0..234e565e 100644
--- a/test/go/command_line_arguments/app.go
+++ b/test/go/command_line_arguments/app.go
@@ -1,23 +1,23 @@
package main
import (
- "io"
- "os"
- "fmt"
- "strings"
- "net/http"
- "nginx/unit"
+ "fmt"
+ "io"
+ "net/http"
+ "nginx/unit"
+ "os"
+ "strings"
)
func handler(w http.ResponseWriter, r *http.Request) {
- args := strings.Join(os.Args[1:], ",")
+ args := strings.Join(os.Args[1:], ",")
- w.Header().Add("X-Arg-0", fmt.Sprintf("%v", os.Args[0]))
- w.Header().Add("Content-Length", fmt.Sprintf("%v", len(args)))
- io.WriteString(w, args)
+ w.Header().Add("X-Arg-0", fmt.Sprintf("%v", os.Args[0]))
+ w.Header().Add("Content-Length", fmt.Sprintf("%v", len(args)))
+ io.WriteString(w, args)
}
func main() {
- http.HandleFunc("/", handler)
- unit.ListenAndServe(":7080", nil)
+ http.HandleFunc("/", handler)
+ unit.ListenAndServe(":7080", nil)
}
diff --git a/test/go/cookies/app.go b/test/go/cookies/app.go
index 6fb9def0..e6647ea8 100644
--- a/test/go/cookies/app.go
+++ b/test/go/cookies/app.go
@@ -1,19 +1,19 @@
package main
import (
- "net/http"
- "nginx/unit"
+ "net/http"
+ "nginx/unit"
)
func handler(w http.ResponseWriter, r *http.Request) {
- cookie1, _ := r.Cookie("var1")
- cookie2, _ := r.Cookie("var2")
+ cookie1, _ := r.Cookie("var1")
+ cookie2, _ := r.Cookie("var2")
- w.Header().Set("X-Cookie-1", cookie1.Value)
- w.Header().Set("X-Cookie-2", cookie2.Value)
+ w.Header().Set("X-Cookie-1", cookie1.Value)
+ w.Header().Set("X-Cookie-2", cookie2.Value)
}
func main() {
- http.HandleFunc("/", handler)
- unit.ListenAndServe(":7080", nil)
+ http.HandleFunc("/", handler)
+ unit.ListenAndServe(":7080", nil)
}
diff --git a/test/go/empty/app.go b/test/go/empty/app.go
index 2e07405f..6e0fce1b 100644
--- a/test/go/empty/app.go
+++ b/test/go/empty/app.go
@@ -1,13 +1,13 @@
package main
import (
- "net/http"
- "nginx/unit"
+ "net/http"
+ "nginx/unit"
)
func handler(w http.ResponseWriter, r *http.Request) {}
func main() {
- http.HandleFunc("/", handler)
- unit.ListenAndServe(":7080", nil)
+ http.HandleFunc("/", handler)
+ unit.ListenAndServe(":7080", nil)
}
diff --git a/test/go/get_variables/app.go b/test/go/get_variables/app.go
index 563febc8..4dcc0e7b 100644
--- a/test/go/get_variables/app.go
+++ b/test/go/get_variables/app.go
@@ -1,17 +1,17 @@
package main
import (
- "net/http"
- "nginx/unit"
+ "net/http"
+ "nginx/unit"
)
func handler(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("X-Var-1", r.URL.Query().Get("var1"))
- w.Header().Set("X-Var-2", r.URL.Query().Get("var2"))
- w.Header().Set("X-Var-3", r.URL.Query().Get("var3"))
+ w.Header().Set("X-Var-1", r.URL.Query().Get("var1"))
+ w.Header().Set("X-Var-2", r.URL.Query().Get("var2"))
+ w.Header().Set("X-Var-3", r.URL.Query().Get("var3"))
}
func main() {
- http.HandleFunc("/", handler)
- unit.ListenAndServe(":7080", nil)
+ http.HandleFunc("/", handler)
+ unit.ListenAndServe(":7080", nil)
}
diff --git a/test/go/mirror/app.go b/test/go/mirror/app.go
index 82b1c92d..748aa7ee 100644
--- a/test/go/mirror/app.go
+++ b/test/go/mirror/app.go
@@ -1,21 +1,21 @@
package main
import (
- "io"
- "fmt"
- "net/http"
- "nginx/unit"
+ "fmt"
+ "io"
+ "net/http"
+ "nginx/unit"
)
func handler(w http.ResponseWriter, r *http.Request) {
- var buf [32768]byte;
- len, _ := r.Body.Read(buf[:])
+ var buf [32768]byte
+ len, _ := r.Body.Read(buf[:])
- w.Header().Add("Content-Length", fmt.Sprintf("%v", len))
- io.WriteString(w, string(buf[:len]))
+ w.Header().Add("Content-Length", fmt.Sprintf("%v", len))
+ io.WriteString(w, string(buf[:len]))
}
func main() {
- http.HandleFunc("/", handler)
- unit.ListenAndServe(":7080", nil)
+ http.HandleFunc("/", handler)
+ unit.ListenAndServe(":7080", nil)
}
diff --git a/test/go/post_variables/app.go b/test/go/post_variables/app.go
index 433afc62..947976d2 100644
--- a/test/go/post_variables/app.go
+++ b/test/go/post_variables/app.go
@@ -1,19 +1,19 @@
package main
import (
- "net/http"
- "nginx/unit"
+ "net/http"
+ "nginx/unit"
)
func handler(w http.ResponseWriter, r *http.Request) {
- r.ParseForm()
+ r.ParseForm()
- w.Header().Set("X-Var-1", r.Form.Get("var1"))
- w.Header().Set("X-Var-2", r.Form.Get("var2"))
- w.Header().Set("X-Var-3", r.Form.Get("var3"))
+ w.Header().Set("X-Var-1", r.Form.Get("var1"))
+ w.Header().Set("X-Var-2", r.Form.Get("var2"))
+ w.Header().Set("X-Var-3", r.Form.Get("var3"))
}
func main() {
- http.HandleFunc("/", handler)
- unit.ListenAndServe(":7080", nil)
+ http.HandleFunc("/", handler)
+ unit.ListenAndServe(":7080", nil)
}
diff --git a/test/go/variables/app.go b/test/go/variables/app.go
index 5db4ac67..fdcbf7e8 100644
--- a/test/go/variables/app.go
+++ b/test/go/variables/app.go
@@ -1,30 +1,30 @@
package main
import (
- "io"
- "fmt"
- "net/http"
- "nginx/unit"
+ "fmt"
+ "io"
+ "net/http"
+ "nginx/unit"
)
func handler(w http.ResponseWriter, r *http.Request) {
- var buf [4096]byte;
- len, _ := r.Body.Read(buf[:])
+ var buf [4096]byte
+ len, _ := r.Body.Read(buf[:])
- w.Header().Set("Request-Method", r.Method)
- w.Header().Set("Request-Uri", r.RequestURI)
- w.Header().Set("Server-Protocol", r.Proto)
- w.Header().Set("Server-Protocol-Major", fmt.Sprintf("%v", r.ProtoMajor))
- w.Header().Set("Server-Protocol-Minor", fmt.Sprintf("%v", r.ProtoMinor))
- w.Header().Set("Content-Length", fmt.Sprintf("%v", len))
- w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
- w.Header().Set("Custom-Header", r.Header.Get("Custom-Header"))
- w.Header().Set("Http-Host", r.Header.Get("Host"))
+ w.Header().Set("Request-Method", r.Method)
+ w.Header().Set("Request-Uri", r.RequestURI)
+ w.Header().Set("Server-Protocol", r.Proto)
+ w.Header().Set("Server-Protocol-Major", fmt.Sprintf("%v", r.ProtoMajor))
+ w.Header().Set("Server-Protocol-Minor", fmt.Sprintf("%v", r.ProtoMinor))
+ w.Header().Set("Content-Length", fmt.Sprintf("%v", len))
+ w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
+ w.Header().Set("Custom-Header", r.Header.Get("Custom-Header"))
+ w.Header().Set("Http-Host", r.Header.Get("Host"))
- io.WriteString(w, string(buf[:len]))
+ io.WriteString(w, string(buf[:len]))
}
func main() {
- http.HandleFunc("/", handler)
- unit.ListenAndServe(":7080", nil)
+ http.HandleFunc("/", handler)
+ unit.ListenAndServe(":7080", nil)
}
diff --git a/test/java/empty_war/empty.war b/test/java/empty_war/empty.war
new file mode 100644
index 00000000..4985e804
--- /dev/null
+++ b/test/java/empty_war/empty.war
Binary files differ
diff --git a/test/java/multipart/app.java b/test/java/multipart/app.java
new file mode 100644
index 00000000..c4c89ffb
--- /dev/null
+++ b/test/java/multipart/app.java
@@ -0,0 +1,93 @@
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import java.util.Map;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.annotation.MultipartConfig;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import javax.servlet.http.Part;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@WebServlet("/")
+@MultipartConfig(
+ fileSizeThreshold = 1024 * 1024 * 1, // 1 MB
+ maxFileSize = 1024 * 1024 * 10, // 10 MB
+ maxRequestSize = 1024 * 1024 * 15 // 15 MB
+)
+public class app extends HttpServlet
+{
+ @Override
+ public void doPost(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException
+ {
+ response.setContentType("text/html;charset=UTF-8");
+
+ // Create path components to save the file
+ final String path = request.getParameter("destination");
+ final Part filePart = request.getPart("file");
+ final String fileName = getFileName(filePart);
+
+ OutputStream out = null;
+ InputStream filecontent = null;
+ final PrintWriter writer = response.getWriter();
+
+ try {
+ out = new FileOutputStream(new File(path + File.separator
+ + fileName));
+ filecontent = filePart.getInputStream();
+
+ int read = 0;
+ final byte[] bytes = new byte[1024];
+
+ while ((read = filecontent.read(bytes)) != -1) {
+ out.write(bytes, 0, read);
+ }
+ writer.println(fileName + " created at " + path);
+
+ } catch (FileNotFoundException fne) {
+ writer.println("You either did not specify a file to upload or are "
+ + "trying to upload a file to a protected or nonexistent "
+ + "location.");
+ writer.println("<br/> ERROR: " + fne.getMessage());
+
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ if (filecontent != null) {
+ filecontent.close();
+ }
+ if (writer != null) {
+ writer.close();
+ }
+ }
+
+ return;
+ }
+
+ private String getFileName(final Part part) {
+ final String partHeader = part.getHeader("content-disposition");
+
+ for (String content : part.getHeader("content-disposition").split(";"))
+ {
+ if (content.trim().startsWith("filename")) {
+ return content.substring(
+ content.indexOf("=") + 1).trim().replace("\"", "");
+ }
+ }
+ return null;
+ }
+}
diff --git a/test/java/session_inactive/app.java b/test/java/session_inactive/app.java
index f338fc89..618e4d67 100644
--- a/test/java/session_inactive/app.java
+++ b/test/java/session_inactive/app.java
@@ -17,7 +17,13 @@ public class app extends HttpServlet
HttpSession s = request.getSession();
if (s.isNew()) {
- s.setMaxInactiveInterval(2);
+ String interval = request.getHeader("X-Interval");
+
+ if (interval == null) {
+ s.setMaxInactiveInterval(0);
+ } else {
+ s.setMaxInactiveInterval(Integer.parseInt(interval));
+ }
}
response.addHeader("X-Session-Id", s.getId());
diff --git a/test/node/404/app.js b/test/node/404/app.js
index 9600d486..587c432d 100755
--- a/test/node/404/app.js
+++ b/test/node/404/app.js
@@ -3,6 +3,5 @@
var fs = require('fs');
require('unit-http').createServer(function (req, res) {
- res.writeHead(404, {});
- res.end(fs.readFileSync('404.html'));
+ res.writeHead(404, {}).end(fs.readFileSync('404.html'));
}).listen(7080);
diff --git a/test/node/basic/app.js b/test/node/basic/app.js
index bc8d570a..7820c474 100755
--- a/test/node/basic/app.js
+++ b/test/node/basic/app.js
@@ -1,6 +1,6 @@
#!/usr/bin/env node
require('unit-http').createServer(function (req, res) {
- res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'});
- res.end('Hello World\n');
+ res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'})
+ .end('Hello World\n');
}).listen(7080);
diff --git a/test/node/double_end/app.js b/test/node/double_end/app.js
index d8280917..63912097 100755
--- a/test/node/double_end/app.js
+++ b/test/node/double_end/app.js
@@ -1,6 +1,5 @@
#!/usr/bin/env node
require('unit-http').createServer(function (req, res) {
- res.end();
- res.end();
+ res.end().end();
}).listen(7080);
diff --git a/test/node/mirror/app.js b/test/node/mirror/app.js
index abcb87cb..1488917e 100755
--- a/test/node/mirror/app.js
+++ b/test/node/mirror/app.js
@@ -6,7 +6,7 @@ require('unit-http').createServer(function (req, res) {
body += chunk.toString();
});
req.on('end', () => {
- res.writeHead(200, {'Content-Length': Buffer.byteLength(body)});
- res.end(body);
+ res.writeHead(200, {'Content-Length': Buffer.byteLength(body)})
+ .end(body);
});
}).listen(7080);
diff --git a/test/node/promise_handler/app.js b/test/node/promise_handler/app.js
index 60b0c3bb..51c3666b 100755
--- a/test/node/promise_handler/app.js
+++ b/test/node/promise_handler/app.js
@@ -6,8 +6,7 @@ require('unit-http').createServer(function (req, res) {
res.end();
if (req.headers['x-write-call']) {
- res.writeHead(200, {'Content-Type': 'text/plain'});
- res.write('blah');
+ res.writeHead(200, {'Content-Type': 'text/plain'}).write('blah');
}
Promise.resolve().then(() => {
diff --git a/test/node/status_message/app.js b/test/node/status_message/app.js
index 4f3b064a..e8a798dd 100755
--- a/test/node/status_message/app.js
+++ b/test/node/status_message/app.js
@@ -1,6 +1,5 @@
#!/usr/bin/env node
require('unit-http').createServer(function (req, res) {
- res.writeHead(200, 'blah', {'Content-Type': 'text/plain'});
- res.end();
+ res.writeHead(200, 'blah', {'Content-Type': 'text/plain'}).end();
}).listen(7080);
diff --git a/test/node/variables/app.js b/test/node/variables/app.js
index 4ed94d09..d8cdc20c 100755
--- a/test/node/variables/app.js
+++ b/test/node/variables/app.js
@@ -14,7 +14,6 @@ require('unit-http').createServer(function (req, res) {
res.setHeader('Content-Type', req.headers['content-type']);
res.setHeader('Custom-Header', req.headers['custom-header']);
res.setHeader('Http-Host', req.headers['host']);
- res.writeHead(200, {});
- res.end(body);
+ res.writeHead(200, {}).end(body);
});
}).listen(7080);
diff --git a/test/node/websockets/mirror/app.js b/test/node/websockets/mirror/app.js
new file mode 100755
index 00000000..23746465
--- /dev/null
+++ b/test/node/websockets/mirror/app.js
@@ -0,0 +1,31 @@
+#!/usr/bin/env node
+
+server = require('unit-http').createServer(function() {});
+webSocketServer = require('unit-http/websocket').server;
+//server = require('http').createServer(function() {});
+//webSocketServer = require('websocket').server;
+
+server.listen(7080, function() {});
+
+var wsServer = new webSocketServer({
+ maxReceivedMessageSize: 0x1000000000,
+ maxReceivedFrameSize: 0x1000000000,
+ fragmentOutgoingMessages: false,
+ fragmentationThreshold: 0x1000000000,
+ httpServer: server,
+});
+
+wsServer.on('request', function(request) {
+ var connection = request.accept(null);
+
+ connection.on('message', function(message) {
+ if (message.type === 'utf8') {
+ connection.send(message.utf8Data);
+ } else if (message.type === 'binary') {
+ connection.send(message.binaryData);
+ }
+
+ });
+
+ connection.on('close', function(r) {});
+});
diff --git a/test/node/websockets/mirror_fragmentation/app.js b/test/node/websockets/mirror_fragmentation/app.js
new file mode 100755
index 00000000..7024252a
--- /dev/null
+++ b/test/node/websockets/mirror_fragmentation/app.js
@@ -0,0 +1,26 @@
+#!/usr/bin/env node
+
+server = require('unit-http').createServer(function() {});
+webSocketServer = require('unit-http/websocket').server;
+//server = require('http').createServer(function() {});
+//webSocketServer = require('websocket').server;
+
+server.listen(7080, function() {});
+
+var wsServer = new webSocketServer({
+ httpServer: server
+});
+
+wsServer.on('request', function(request) {
+ //console.log('request');
+ var connection = request.accept(null);
+
+ connection.on('message', function(message) {
+ //console.log('message');
+ connection.send(message.utf8Data);
+ });
+
+ connection.on('close', function(r) {
+ //console.log('close');
+ });
+});
diff --git a/test/node/write_before_write_head/app.js b/test/node/write_before_write_head/app.js
index 6e3fb9a9..724b0efb 100755
--- a/test/node/write_before_write_head/app.js
+++ b/test/node/write_before_write_head/app.js
@@ -2,6 +2,5 @@
require('unit-http').createServer(function (req, res) {
res.write('blah');
- res.writeHead(200, {'Content-Type': 'text/plain'});
- res.end();
+ res.writeHead(200, {'Content-Type': 'text/plain'}).end();
}).listen(7080);
diff --git a/test/node/write_buffer/app.js b/test/node/write_buffer/app.js
index f41de2a1..a7623523 100755
--- a/test/node/write_buffer/app.js
+++ b/test/node/write_buffer/app.js
@@ -1,6 +1,6 @@
#!/usr/bin/env node
require('unit-http').createServer(function (req, res) {
- res.writeHead(200, {'Content-Type': 'text/plain'});
- res.end(new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]));
+ res.writeHead(200, {'Content-Type': 'text/plain'})
+ .end(new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]));
}).listen(7080);
diff --git a/test/node/write_return/app.js b/test/node/write_return/app.js
index 3ae967c6..82dfbc6e 100755
--- a/test/node/write_return/app.js
+++ b/test/node/write_return/app.js
@@ -1,6 +1,6 @@
#!/usr/bin/env node
require('unit-http').createServer(function (req, res) {
- res.writeHead(200, {'Content-Type': 'text/plain'});
- res.end(res.write('body').toString());
+ res.writeHead(200, {'Content-Type': 'text/plain'})
+ .end(res.write('body').toString());
}).listen(7080);
diff --git a/test/php/header/index.php b/test/php/header/index.php
new file mode 100644
index 00000000..1aa5ca04
--- /dev/null
+++ b/test/php/header/index.php
@@ -0,0 +1,4 @@
+<?php
+header($_SERVER['HTTP_X_HEADER']);
+header('Content-Length: 0');
+?>
diff --git a/test/php/script/phpinfo.php b/test/php/script/phpinfo.php
new file mode 100644
index 00000000..cf608608
--- /dev/null
+++ b/test/php/script/phpinfo.php
@@ -0,0 +1,3 @@
+<?php
+phpinfo();
+?>
diff --git a/test/php/variables/index.php b/test/php/variables/index.php
index 8f2e3bfc..279efc79 100644
--- a/test/php/variables/index.php
+++ b/test/php/variables/index.php
@@ -4,6 +4,7 @@ $body = file_get_contents('php://input');
header('Content-Length: ' . strlen($body));
header('Request-Method: ' . $_SERVER['REQUEST_METHOD']);
header('Request-Uri: ' . $_SERVER['REQUEST_URI']);
+header('Path-Info: ' . $_SERVER['PATH_INFO']);
header('Http-Host: ' . $_SERVER['HTTP_HOST']);
header('Server-Protocol: ' . $_SERVER['SERVER_PROTOCOL']);
header('Server-Software: ' . $_SERVER['SERVER_SOFTWARE']);
diff --git a/test/test_access_log.py b/test/test_access_log.py
index 49497ad2..fbcc131f 100644
--- a/test/test_access_log.py
+++ b/test/test_access_log.py
@@ -180,7 +180,9 @@ Connection: close
self.assertEqual(self.post()['status'], 200, 'init')
- resp = self.http(b"""GE""", raw=True, read_timeout=5)
+ resp = self.http(b"""GE""", raw=True, read_timeout=1)
+
+ time.sleep(1)
self.stop()
@@ -206,7 +208,9 @@ Connection: close
self.assertEqual(self.post()['status'], 200, 'init')
- resp = self.http(b"""GET / HTTP/1.1""", raw=True, read_timeout=5)
+ resp = self.http(b"""GET / HTTP/1.1""", raw=True, read_timeout=1)
+
+ time.sleep(1)
self.stop()
@@ -219,7 +223,9 @@ Connection: close
self.assertEqual(self.post()['status'], 200, 'init')
- resp = self.http(b"""GET / HTTP/1.1\n""", raw=True, read_timeout=5)
+ resp = self.http(b"""GET / HTTP/1.1\n""", raw=True, read_timeout=1)
+
+ time.sleep(1)
self.stop()
diff --git a/test/test_java_application.py b/test/test_java_application.py
index 5d0350fa..526be565 100644
--- a/test/test_java_application.py
+++ b/test/test_java_application.py
@@ -1,10 +1,52 @@
import time
+import unittest
from unit.applications.lang.java import TestApplicationJava
class TestJavaApplication(TestApplicationJava):
prerequisites = ['java']
+ def test_java_conf_error(self):
+ self.skip_alerts.extend(
+ [
+ r'realpath.*failed',
+ r'failed to apply new conf',
+ ]
+ )
+ self.assertIn(
+ 'error',
+ self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/app"}},
+ "applications": {
+ "app": {
+ "type": "java",
+ "processes": 1,
+ "working_directory": self.current_dir
+ + "/java/empty",
+ "webapp": self.testdir + "/java",
+ "unit_jars": self.testdir + "/no_such_dir",
+ }
+ },
+ }
+ ),
+ 'conf error',
+ )
+
+ def test_java_war(self):
+ self.load('empty_war')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ '"' + self.testdir + '/java/empty.war"',
+ '/config/applications/empty_war/webapp',
+ ),
+ 'configure war',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'war')
+
def test_java_application_cookies(self):
self.load('cookies')
@@ -99,12 +141,16 @@ class TestJavaApplication(TestApplicationJava):
def test_java_application_session_active(self):
self.load('session_inactive')
- resp = self.get()
+ resp = self.get(headers={
+ 'X-Interval': '4',
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ })
session_id = resp['headers']['X-Session-Id']
self.assertEqual(resp['status'], 200, 'session init')
self.assertEqual(
- resp['headers']['X-Session-Interval'], '2', 'session interval'
+ resp['headers']['X-Session-Interval'], '4', 'session interval'
)
self.assertLess(
abs(
@@ -147,7 +193,7 @@ class TestJavaApplication(TestApplicationJava):
resp['headers']['X-Session-Id'], session_id, 'session active 2'
)
- time.sleep(1)
+ time.sleep(2)
resp = self.get(
headers={
@@ -164,7 +210,11 @@ class TestJavaApplication(TestApplicationJava):
def test_java_application_session_inactive(self):
self.load('session_inactive')
- resp = self.get()
+ resp = self.get(headers={
+ 'X-Interval': '1',
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ })
session_id = resp['headers']['X-Session-Id']
time.sleep(3)
@@ -1164,6 +1214,43 @@ class TestJavaApplication(TestApplicationJava):
)
self.assertEqual(headers['X-Get-Date'], date, 'get date header')
+ def test_java_application_multipart(self):
+ self.load('multipart')
+
+ body = """Preamble. Should be ignored.\r
+\r
+--12345\r
+Content-Disposition: form-data; name="file"; filename="sample.txt"\r
+Content-Type: text/plain\r
+\r
+Data from sample file\r
+--12345\r
+Content-Disposition: form-data; name="destination"\r
+\r
+%s\r
+--12345\r
+Content-Disposition: form-data; name="upload"\r
+\r
+Upload\r
+--12345--\r
+\r
+Epilogue. Should be ignored.""" % self.testdir
+
+ resp = self.post(
+ headers={
+ 'Content-Type': 'multipart/form-data; boundary=12345',
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ },
+ body=body,
+ )
+
+ self.assertEqual(resp['status'], 200, 'multipart status')
+ self.assertRegex(resp['body'], r'sample\.txt created', 'multipart body')
+ self.assertIsNotNone(
+ self.search_in_log(r'^Data from sample file$', name='sample.txt'),
+ 'file created',
+ )
if __name__ == '__main__':
TestJavaApplication.main()
diff --git a/test/test_node_websockets.py b/test/test_node_websockets.py
new file mode 100644
index 00000000..6652d8c5
--- /dev/null
+++ b/test/test_node_websockets.py
@@ -0,0 +1,1585 @@
+import time
+import struct
+import unittest
+from unit.applications.lang.node import TestApplicationNode
+from unit.applications.websockets import TestApplicationWebsocket
+
+class TestNodeWebsockets(TestApplicationNode):
+ prerequisites = ['node']
+
+ ws = TestApplicationWebsocket()
+
+ def setUp(self):
+ super().setUp()
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {'http': {'websocket': {'keepalive_interval': 0}}}, 'settings'
+ ),
+ 'clear keepalive_interval',
+ )
+
+ self.skip_alerts.extend(
+ [
+ r'last message send failed',
+ r'socket close\(\d+\) failed',
+ ]
+ )
+
+ def close_connection(self, sock):
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+
+ self.check_close(sock)
+
+ def check_close(self, sock, code = 1000, no_close = False):
+ frame = self.ws.frame_read(sock)
+
+ self.assertEqual(frame['fin'], True, 'close fin')
+ self.assertEqual(frame['opcode'], self.ws.OP_CLOSE, 'close opcode')
+ self.assertEqual(frame['code'], code, 'close code')
+
+ if not no_close:
+ sock.close()
+
+ def check_frame(self, frame, fin, opcode, payload, decode=True):
+ if opcode == self.ws.OP_BINARY or not decode:
+ data = frame['data']
+ else:
+ data = frame['data'].decode('utf-8')
+
+ self.assertEqual(frame['fin'], fin, 'fin')
+ self.assertEqual(frame['opcode'], opcode, 'opcode')
+ self.assertEqual(data, payload, 'payload')
+
+ def test_node_websockets_handshake(self):
+ self.load('websockets/mirror')
+
+ resp, sock, key = self.ws.upgrade()
+ sock.close()
+
+ self.assertEqual(resp['status'], 101, 'status')
+ self.assertEqual(
+ resp['headers']['Upgrade'], 'websocket', 'upgrade'
+ )
+ self.assertEqual(
+ resp['headers']['Connection'], 'Upgrade', 'connection'
+ )
+ self.assertEqual(
+ resp['headers']['Sec-WebSocket-Accept'], self.ws.accept(key), 'key'
+ )
+
+ def test_node_websockets_mirror(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message)
+ frame = self.ws.frame_read(sock)
+
+ self.assertEqual(
+ message, frame['data'].decode('utf-8'), 'mirror'
+ )
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message)
+ frame = self.ws.frame_read(sock)
+
+ self.assertEqual(
+ message, frame['data'].decode('utf-8'), 'mirror 2'
+ )
+
+ sock.close()
+
+ def test_node_websockets_no_mask(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message, mask=False)
+
+ frame = self.ws.frame_read(sock)
+
+ self.assertEqual(frame['opcode'], self.ws.OP_CLOSE, 'no mask opcode')
+ self.assertEqual(frame['code'], 1002, 'no mask close code')
+
+ sock.close()
+
+ def test_node_websockets_fragmentation(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message, fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, ' ', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, message)
+
+ frame = self.ws.frame_read(sock)
+
+ self.assertEqual(
+ message + ' ' + message,
+ frame['data'].decode('utf-8'),
+ 'mirror framing',
+ )
+
+ sock.close()
+
+ def test_node_websockets_frame_fragmentation_invalid(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_PING, message, fin=False)
+
+ frame = self.ws.frame_read(sock)
+
+ frame.pop('data')
+ self.assertDictEqual(
+ frame,
+ {
+ 'fin': True,
+ 'rsv1': False,
+ 'rsv2': False,
+ 'rsv3': False,
+ 'opcode': self.ws.OP_CLOSE,
+ 'mask': 0,
+ 'code': 1002,
+ 'reason': 'Fragmented control frame',
+ },
+ 'close frame',
+ )
+
+ sock.close()
+
+ def test_node_websockets_partial_send(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ frame = self.ws.frame_to_send(self.ws.OP_TEXT, message)
+ sock.sendall(frame[:1])
+ sock.sendall(frame[1:2])
+ sock.sendall(frame[2:3])
+ sock.sendall(frame[3:])
+
+ frame = self.ws.frame_read(sock)
+
+ self.assertEqual(
+ message,
+ frame['data'].decode('utf-8'),
+ 'partial send',
+ )
+
+ sock.close()
+
+ def test_node_websockets_large(self):
+ self.load('websockets/mirror_fragmentation')
+
+ message = '0123456789' * 3000
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message)
+
+ frame = self.ws.frame_read(sock)
+ data = frame['data'].decode('utf-8')
+
+ frame = self.ws.frame_read(sock)
+ data += frame['data'].decode('utf-8')
+
+ self.assertEqual(message, data, 'large')
+
+ sock.close()
+
+ def test_node_websockets_frame_invalid_opcode(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message, fin=False)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message)
+
+ frame = self.ws.frame_read(sock)
+
+ frame.pop('data')
+ frame.pop('reason')
+ self.assertDictEqual(
+ frame,
+ {
+ 'fin': True,
+ 'rsv1': False,
+ 'rsv2': False,
+ 'rsv3': False,
+ 'opcode': self.ws.OP_CLOSE,
+ 'mask': 0,
+ 'code': 1002,
+ },
+ 'close frame',
+ )
+
+ sock.close()
+
+ def test_node_websockets_frame_invalid_opcode_2(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CONT, message)
+
+ frame = self.ws.frame_read(sock)
+
+ frame.pop('data')
+ self.assertDictEqual(
+ frame,
+ {
+ 'fin': True,
+ 'rsv1': False,
+ 'rsv2': False,
+ 'rsv3': False,
+ 'opcode': self.ws.OP_CLOSE,
+ 'mask': 0,
+ 'code': 1002,
+ 'reason': 'Unrecognized opcode 0',
+ },
+ 'close frame',
+ )
+
+ sock.close()
+
+ def test_node_websockets_two_clients(self):
+ self.load('websockets/mirror')
+
+ message1 = 'blah1'
+ message2 = 'blah2'
+
+ _, sock1, _ = self.ws.upgrade()
+ _, sock2, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock1, self.ws.OP_TEXT, message1)
+ self.ws.frame_write(sock2, self.ws.OP_TEXT, message2)
+
+ frame1 = self.ws.frame_read(sock1)
+ frame2 = self.ws.frame_read(sock2)
+
+ self.assertEqual(
+ message1, frame1['data'].decode('utf-8'), 'client 1'
+ )
+ self.assertEqual(
+ message2, frame2['data'].decode('utf-8'), 'client 2'
+ )
+
+ sock1.close()
+ sock2.close()
+
+ @unittest.skip('not yet')
+ def test_node_websockets_handshake_upgrade_absent(self): # FAIL https://tools.ietf.org/html/rfc6455#section-4.2.1
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13,
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'upgrade absent')
+
+ def test_node_websockets_handshake_case_insensitive(self):
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'WEBSOCKET',
+ 'Connection': 'UPGRADE',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13,
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 101, 'status')
+
+ @unittest.skip('not yet')
+ def test_node_websockets_handshake_connection_absent(self): # FAIL
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13,
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'status')
+
+ def test_node_websockets_handshake_version_absent(self):
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat'
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 426, 'status')
+
+ @unittest.skip('not yet')
+ def test_node_websockets_handshake_key_invalid(self):
+ self.load('websockets/mirror')
+
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': '!',
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'key length')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': [key, key],
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'key double') # FAIL https://tools.ietf.org/html/rfc6455#section-11.3.1
+
+ def test_node_websockets_handshake_method_invalid(self):
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.post(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'status')
+
+ def test_node_websockets_handshake_http_10(self):
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13
+ }, http_10=True, read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'status')
+
+ def test_node_websockets_handshake_uri_invalid(self):
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13
+ }, url='!', read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'status')
+
+ def test_node_websockets_protocol_absent(self):
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Version': 13
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 101, 'status')
+ self.assertEqual(
+ resp['headers']['Upgrade'], 'websocket', 'upgrade'
+ )
+ self.assertEqual(
+ resp['headers']['Connection'], 'Upgrade', 'connection'
+ )
+ self.assertEqual(
+ resp['headers']['Sec-WebSocket-Accept'], self.ws.accept(key), 'key'
+ )
+
+ # autobahn-testsuite
+
+ # Some following tests fail because of Unit does not support UTF-8
+ # validation for websocket frames. It should be implemented
+ # by application, if necessary.
+
+ @unittest.skip('not yet')
+ def test_node_websockets_1_1_1__1_1_8(self):
+ self.load('websockets/mirror')
+
+ opcode = self.ws.OP_TEXT
+
+ _, sock, _ = self.ws.upgrade()
+
+ def check_length(length, chopsize=None):
+ payload = '*' * length
+
+ self.ws.frame_write(sock, opcode, payload, chopsize=chopsize)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, opcode, payload)
+
+ check_length(0) # 1_1_1
+ check_length(125) # 1_1_2
+ check_length(126) # 1_1_3
+ check_length(127) # 1_1_4
+ check_length(128) # 1_1_5
+ check_length(65535) # 1_1_6
+ check_length(65536) # 1_1_7
+ check_length(65536, chopsize = 997) # 1_1_8
+
+ self.close_connection(sock)
+
+ @unittest.skip('not yet')
+ def test_node_websockets_1_2_1__1_2_8(self):
+ self.load('websockets/mirror')
+
+ opcode = self.ws.OP_BINARY
+
+ _, sock, _ = self.ws.upgrade()
+
+ def check_length(length, chopsize=None):
+ payload = b'\xfe' * length
+
+ self.ws.frame_write(sock, opcode, payload, chopsize=chopsize)
+ frame = self.ws.frame_read(sock)
+
+ self.check_frame(frame, True, opcode, payload)
+
+ check_length(0) # 1_2_1
+ check_length(125) # 1_2_2
+ check_length(126) # 1_2_3
+ check_length(127) # 1_2_4
+ check_length(128) # 1_2_5
+ check_length(65535) # 1_2_6
+ check_length(65536) # 1_2_7
+ check_length(65536, chopsize = 997) # 1_2_8
+
+ self.close_connection(sock)
+
+ def test_node_websockets_2_1__2_6(self):
+ self.load('websockets/mirror')
+
+ op_ping = self.ws.OP_PING
+ op_pong = self.ws.OP_PONG
+
+ _, sock, _ = self.ws.upgrade()
+
+ def check_ping(payload, chopsize=None, decode=True):
+ self.ws.frame_write(sock, op_ping, payload, chopsize=chopsize)
+ frame = self.ws.frame_read(sock)
+
+ self.check_frame(frame, True, op_pong, payload, decode=decode)
+
+ check_ping('') # 2_1
+ check_ping('Hello, world!') # 2_2
+ check_ping(b'\x00\xff\xfe\xfd\xfc\xfb\x00\xff', decode=False) # 2_3
+ check_ping(b'\xfe' * 125, decode=False) # 2_4
+ check_ping(b'\xfe' * 125, chopsize=1, decode=False) # 2_6
+
+ self.close_connection(sock)
+
+ # 2_5
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_PING, b'\xfe' * 126)
+ self.check_close(sock, 1002)
+
+ def test_node_websockets_2_7__2_9(self):
+ self.load('websockets/mirror')
+
+ # 2_7
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_PONG, '')
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', '2_7')
+
+ # 2_8
+
+ self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload')
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', '2_8')
+
+ # 2_9
+
+ payload = 'ping payload'
+
+ self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload')
+ self.ws.frame_write(sock, self.ws.OP_PING, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, payload)
+
+ self.close_connection(sock)
+
+ def test_node_websockets_2_10__2_11(self):
+ self.load('websockets/mirror')
+
+ # 2_10
+
+ _, sock, _ = self.ws.upgrade()
+
+ for i in range(0, 10):
+ self.ws.frame_write(sock, self.ws.OP_PING, 'payload-%d' % i)
+
+ for i in range(0, 10):
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, 'payload-%d' % i)
+
+ # 2_11
+
+ for i in range(0, 10):
+ opcode = self.ws.OP_PING
+ self.ws.frame_write(sock, opcode, 'payload-%d' % i, chopsize=1)
+
+ for i in range(0, 10):
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, 'payload-%d' % i)
+
+ self.close_connection(sock)
+
+ @unittest.skip('not yet')
+ def test_node_websockets_3_1__3_7(self):
+ self.load('websockets/mirror')
+
+ payload = 'Hello, world!'
+
+ # 3_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload, rsv1=True)
+ self.check_close(sock, 1002)
+
+ # 3_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload, rsv2=True)
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.check_close(sock, 1002, no_close = True)
+
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_2')
+ sock.close()
+
+ # 3_3
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_TEXT,
+ payload,
+ rsv1=True,
+ rsv2=True,
+ )
+
+ self.check_close(sock, 1002, no_close = True)
+
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_3')
+ sock.close()
+
+ # 3_4
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload, chopsize=1)
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_TEXT,
+ payload,
+ rsv3=True,
+ chopsize=1
+ )
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.check_close(sock, 1002, no_close = True)
+
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_4')
+ sock.close()
+
+ # 3_5
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_BINARY,
+ b'\x00\xff\xfe\xfd\xfc\xfb\x00\xff',
+ rsv1=True,
+ rsv3=True,
+ )
+
+ self.check_close(sock, 1002)
+
+ # 3_6
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_PING,
+ payload,
+ rsv2=True,
+ rsv3=True,
+ )
+
+ self.check_close(sock, 1002)
+
+ # 3_7
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CLOSE,
+ payload,
+ rsv1=True,
+ rsv2=True,
+ rsv3=True,
+ )
+
+ self.check_close(sock, 1002)
+
+ def test_node_websockets_4_1_1__4_2_5(self):
+ self.load('websockets/mirror')
+
+ payload = 'Hello, world!'
+
+ # 4_1_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, 0x03, '')
+ self.check_close(sock, 1002)
+
+ # 4_1_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, 0x04, 'reserved opcode payload')
+ self.check_close(sock, 1002)
+
+ # 4_1_3
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(sock, 0x05, '')
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ self.check_close(sock, 1002)
+
+ # 4_1_4
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(sock, 0x06, payload)
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ self.check_close(sock, 1002)
+
+ # 4_1_5
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload, chopsize=1)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(sock, 0x07, payload, chopsize=1)
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ self.check_close(sock, 1002)
+
+ # 4_2_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, 0x0B, '')
+ self.check_close(sock, 1002)
+
+ # 4_2_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, 0x0C, 'reserved opcode payload')
+ self.check_close(sock, 1002)
+
+ # 4_2_3
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(sock, 0x0D, '')
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ self.check_close(sock, 1002)
+
+ # 4_2_4
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(sock, 0x0E, payload)
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ self.check_close(sock, 1002)
+
+ # 4_2_5
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload, chopsize=1)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(sock, 0x0F, payload, chopsize=1)
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ self.check_close(sock, 1002)
+
+ @unittest.skip('not yet')
+ def test_node_websockets_5_1__5_20(self):
+ self.load('websockets/mirror')
+
+ # 5_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_PING, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_PONG, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_3
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
+
+ # 5_4
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', '5_4')
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
+
+ # 5_5
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_TEXT,
+ 'fragment1',
+ fin=False,
+ chopsize=1,
+ )
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'fragment2',
+ fin=True,
+ chopsize=1,
+ )
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
+
+ # 5_6
+
+ ping_payload = 'ping payload'
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_PING, ping_payload)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, ping_payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
+
+ # 5_7
+
+ ping_payload = 'ping payload'
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', '5_7')
+
+ self.ws.frame_write(sock, self.ws.OP_PING, ping_payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, ping_payload)
+
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
+
+ # 5_8
+
+ ping_payload = 'ping payload'
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_TEXT,
+ 'fragment1',
+ fin=False,
+ chopsize=1,
+ )
+ self.ws.frame_write(sock, self.ws.OP_PING, ping_payload, chopsize=1)
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'fragment2',
+ fin=True,
+ chopsize=1,
+ )
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, ping_payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
+
+ # 5_9
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'non-continuation payload',
+ fin=True,
+ )
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_10
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'non-continuation payload',
+ fin=True,
+ )
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_11
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'non-continuation payload',
+ fin=True,
+ chopsize=1,
+ )
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_TEXT,
+ 'Hello, world!',
+ fin=True,
+ chopsize=1,
+ )
+ self.check_close(sock, 1002)
+
+ # 5_12
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'non-continuation payload',
+ fin=False,
+ )
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_13
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'non-continuation payload',
+ fin=False,
+ )
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_14
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'non-continuation payload',
+ fin=False,
+ chopsize=1,
+ )
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_TEXT,
+ 'Hello, world!',
+ fin=True,
+ chopsize=1,
+ )
+ self.check_close(sock, 1002)
+
+ # 5_15
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment4', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_16
+
+ _, sock, _ = self.ws.upgrade()
+
+ for i in range(0, 2):
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment2', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_17
+
+ _, sock, _ = self.ws.upgrade()
+
+ for i in range(0, 2):
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment1', fin=True)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment2', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_18
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment2')
+ self.check_close(sock, 1002)
+
+ # 5_19
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 1!')
+
+ time.sleep(1)
+
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment4', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 2!')
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 1!')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!')
+
+ self.check_frame(
+ self.ws.frame_read(sock),
+ True,
+ self.ws.OP_TEXT,
+ 'fragment1fragment2fragment3fragment4fragment5',
+ )
+
+ # 5_20
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 1!')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 1!')
+
+ time.sleep(1)
+
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment4', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 2!')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!')
+
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', '5_20')
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5')
+
+ self.check_frame(
+ self.ws.frame_read(sock),
+ True,
+ self.ws.OP_TEXT,
+ 'fragment1fragment2fragment3fragment4fragment5',
+ )
+
+ self.close_connection(sock)
+
+ def test_node_websockets_6_1_1__6_4_4(self):
+ self.load('websockets/mirror')
+
+ # 6_1_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, '')
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, '')
+
+ # 6_1_2
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, '', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, '', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, '')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, '')
+
+ # 6_1_3
+
+ payload = 'middle frame payload'
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, '', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, payload, fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, '')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ # 6_2_1
+
+ payload = 'Hello-µ@ßöäüàá-UTF-8!!'
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ # 6_2_2
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload[:12], fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, payload[12:])
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ # 6_2_3
+
+ self.ws.message(sock, self.ws.OP_TEXT, payload, fragmention_size=1)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ # 6_2_4
+
+ payload = '\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5'
+
+ self.ws.message(sock, self.ws.OP_TEXT, payload, fragmention_size=1)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.close_connection(sock)
+
+ # Unit does not support UTF-8 validation
+
+# # 6_3_1 FAIL
+#
+# payload_1 = '\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5'
+# payload_2 = '\xed\xa0\x80'
+# payload_3 = '\x65\x64\x69\x74\x65\x64'
+#
+# payload = payload_1 + payload_2 + payload_3
+#
+# self.ws.message(sock, self.ws.OP_TEXT, payload)
+# self.check_close(sock, 1007)
+#
+# # 6_3_2 FAIL
+#
+# _, sock, _ = self.ws.upgrade()
+#
+# self.ws.message(sock, self.ws.OP_TEXT, payload, fragmention_size=1)
+# self.check_close(sock, 1007)
+#
+# # 6_4_1 ... 6_4_4 FAIL
+
+ def test_node_websockets_7_1_1__7_5_1(self):
+ self.load('websockets/mirror')
+
+ # 7_1_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = "Hello World!"
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.close_connection(sock)
+
+ # 7_1_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+
+ self.check_close(sock)
+
+ # 7_1_3
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+ self.check_close(sock, no_close = True)
+
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+
+ sock.close()
+
+ # 7_1_4
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+ self.check_close(sock, no_close = True)
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+
+ sock.close()
+
+ # 7_1_5
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+ self.check_close(sock, no_close = True)
+
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2')
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+
+ sock.close()
+
+ # 7_1_6
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'BAsd7&jh23' * 26 * 2**10)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+
+ self.recvall(sock, read_timeout=1)
+
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+
+ sock.close()
+
+ # 7_3_1 # FAIL
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, '')
+ self.check_close(sock)
+
+ # 7_3_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, 'a')
+ self.check_close(sock, 1002)
+
+ # 7_3_3
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+ self.check_close(sock)
+
+ # 7_3_4
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = self.ws.serialize_close(reason = 'Hello World!')
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock)
+
+ # 7_3_5
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = self.ws.serialize_close(reason = '*' * 123)
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock)
+
+ # 7_3_6
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = self.ws.serialize_close(reason = '*' * 124)
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock, 1002)
+
+ # 7_5_1 FAIL Unit does not support UTF-8 validation
+
+# _, sock, _ = self.ws.upgrade()
+#
+# payload = self.ws.serialize_close(reason = '\xce\xba\xe1\xbd\xb9\xcf' \
+# '\x83\xce\xbc\xce\xb5\xed\xa0\x80\x65\x64\x69\x74\x65\x64')
+#
+# self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+# self.check_close(sock, 1007)
+
+ def test_node_websockets_7_7_X__7_9_X(self):
+ self.load('websockets/mirror')
+
+ valid_codes = [
+ 1000,
+ 1001,
+ 1002,
+ 1003,
+ 1007,
+ 1008,
+ 1009,
+ 1010,
+ 1011,
+ 3000,
+ 3999,
+ 4000,
+ 4999,
+ ]
+
+ invalid_codes = [0, 999, 1004, 1005, 1006, 1016, 1100, 2000, 2999]
+
+ for code in valid_codes:
+ _, sock, _ = self.ws.upgrade()
+
+ payload = self.ws.serialize_close(code = code)
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock)
+
+ for code in invalid_codes:
+ _, sock, _ = self.ws.upgrade()
+
+ payload = self.ws.serialize_close(code = code)
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock, 1002)
+
+ def test_node_websockets_7_13_1__7_13_2(self):
+ self.load('websockets/mirror')
+
+ # 7_13_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = self.ws.serialize_close(code = 5000)
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock, 1002)
+
+ # 7_13_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = struct.pack('!I', 65536) + ''.encode('utf-8')
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock, 1002)
+
+ def test_node_websockets_9_1_1__9_6_6(self):
+ if not self.unsafe:
+ self.skipTest("unsafe, long run")
+
+ self.load('websockets/mirror')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ 'http': {
+ 'websocket': {
+ 'max_frame_size': 33554432,
+ 'keepalive_interval': 0,
+ }
+ }
+ },
+ 'settings',
+ ),
+ 'increase max_frame_size and keepalive_interval',
+ )
+
+ _, sock, _ = self.ws.upgrade()
+
+ op_text = self.ws.OP_TEXT
+ op_binary = self.ws.OP_BINARY
+
+ def check_payload(opcode, length, chopsize=None):
+ if opcode == self.ws.OP_TEXT:
+ payload = '*' * length
+ else:
+ payload = b'*' * length
+
+ self.ws.frame_write(sock, opcode, payload, chopsize=chopsize)
+ frame = self.ws.frame_read(sock, read_timeout=5)
+ self.check_frame(frame, True, opcode, payload)
+
+ def check_message(opcode, f_size):
+ if opcode == self.ws.OP_TEXT:
+ payload = '*' * 4 * 2**20
+ else:
+ payload = b'*' * 4 * 2**20
+
+ self.ws.message(sock, opcode, payload, fragmention_size=f_size)
+ frame = self.ws.frame_read(sock, read_timeout=5)
+ self.check_frame(frame, True, opcode, payload)
+
+ check_payload(op_text, 64 * 2**10) # 9_1_1
+ check_payload(op_text, 256 * 2**10) # 9_1_2
+ check_payload(op_text, 2**20) # 9_1_3
+ check_payload(op_text, 4 * 2**20) # 9_1_4
+ check_payload(op_text, 8 * 2**20) # 9_1_5
+ check_payload(op_text, 16 * 2**20) # 9_1_6
+
+ check_payload(op_binary, 64 * 2**10) # 9_2_1
+ check_payload(op_binary, 256 * 2**10) # 9_2_2
+ check_payload(op_binary, 2**20) # 9_2_3
+ check_payload(op_binary, 4 * 2**20) # 9_2_4
+ check_payload(op_binary, 8 * 2**20) # 9_2_5
+ check_payload(op_binary, 16 * 2**20) # 9_2_6
+
+ if self.system != 'Darwin' and self.system != 'FreeBSD':
+ check_message(op_text, 64) # 9_3_1
+ check_message(op_text, 256) # 9_3_2
+ check_message(op_text, 2**10) # 9_3_3
+ check_message(op_text, 4 * 2**10) # 9_3_4
+ check_message(op_text, 16 * 2**10) # 9_3_5
+ check_message(op_text, 64 * 2**10) # 9_3_6
+ check_message(op_text, 256 * 2**10) # 9_3_7
+ check_message(op_text, 2**20) # 9_3_8
+ check_message(op_text, 4 * 2**20) # 9_3_9
+
+ check_message(op_binary, 64) # 9_4_1
+ check_message(op_binary, 256) # 9_4_2
+ check_message(op_binary, 2**10) # 9_4_3
+ check_message(op_binary, 4 * 2**10) # 9_4_4
+ check_message(op_binary, 16 * 2**10) # 9_4_5
+ check_message(op_binary, 64 * 2**10) # 9_4_6
+ check_message(op_binary, 256 * 2**10) # 9_4_7
+ check_message(op_binary, 2**20) # 9_4_8
+ check_message(op_binary, 4 * 2**20) # 9_4_9
+
+ check_payload(op_text, 2**20, chopsize=64) # 9_5_1
+ check_payload(op_text, 2**20, chopsize=128) # 9_5_2
+ check_payload(op_text, 2**20, chopsize=256) # 9_5_3
+ check_payload(op_text, 2**20, chopsize=512) # 9_5_4
+ check_payload(op_text, 2**20, chopsize=1024) # 9_5_5
+ check_payload(op_text, 2**20, chopsize=2048) # 9_5_6
+
+ check_payload(op_binary, 2**20, chopsize=64) # 9_6_1
+ check_payload(op_binary, 2**20, chopsize=128) # 9_6_2
+ check_payload(op_binary, 2**20, chopsize=256) # 9_6_3
+ check_payload(op_binary, 2**20, chopsize=512) # 9_6_4
+ check_payload(op_binary, 2**20, chopsize=1024) # 9_6_5
+ check_payload(op_binary, 2**20, chopsize=2048) # 9_6_6
+
+ self.close_connection(sock)
+
+ def test_node_websockets_10_1_1(self):
+ self.load('websockets/mirror')
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = '*' * 65536
+
+ self.ws.message(sock, self.ws.OP_TEXT, payload, fragmention_size=1300)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.close_connection(sock)
+
+ # settings
+
+ def test_node_websockets_max_frame_size(self):
+ self.load('websockets/mirror')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {'http': {'websocket': {'max_frame_size': 100}}}, 'settings'
+ ),
+ 'configure max_frame_size',
+ )
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = '*' * 94
+ opcode = self.ws.OP_TEXT
+
+ self.ws.frame_write(sock, opcode, payload) # frame length is 100
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, opcode, payload)
+
+ payload = '*' * 95
+
+ self.ws.frame_write(sock, opcode, payload) # frame length is 101
+ self.check_close(sock, 1009) # 1009 - CLOSE_TOO_LARGE
+
+ def test_node_websockets_read_timeout(self):
+ self.load('websockets/mirror')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {'http': {'websocket': {'read_timeout': 5}}}, 'settings'
+ ),
+ 'configure read_timeout',
+ )
+
+ _, sock, _ = self.ws.upgrade()
+
+ frame = self.ws.frame_to_send(self.ws.OP_TEXT, 'blah')
+ sock.sendall(frame[:2])
+
+ time.sleep(2)
+
+ self.check_close(sock, 1001) # 1001 - CLOSE_GOING_AWAY
+
+ def test_node_websockets_keepalive_interval(self):
+ self.load('websockets/mirror')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {'http': {'websocket': {'keepalive_interval': 5}}}, 'settings'
+ ),
+ 'configure keepalive_interval',
+ )
+
+ _, sock, _ = self.ws.upgrade()
+
+ frame = self.ws.frame_to_send(self.ws.OP_TEXT, 'blah')
+ sock.sendall(frame[:2])
+
+ time.sleep(2)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PING, '') # PING frame
+
+ sock.close()
+
+if __name__ == '__main__':
+ TestNodeWebsockets.main()
diff --git a/test/test_php_application.py b/test/test_php_application.py
index 8032e96e..ee2048b5 100644
--- a/test/test_php_application.py
+++ b/test/test_php_application.py
@@ -24,6 +24,7 @@ class TestPHPApplication(TestApplicationPHP):
'Connection': 'close',
},
body=body,
+ url='/index.php/blah?var=val'
)
self.assertEqual(resp['status'], 200, 'status')
@@ -54,7 +55,8 @@ class TestPHPApplication(TestApplicationPHP):
'Connection': 'close',
'Content-Length': str(len(body)),
'Request-Method': 'POST',
- 'Request-Uri': '/',
+ 'Path-Info': '/blah',
+ 'Request-Uri': '/index.php/blah?var=val',
'Http-Host': 'localhost',
'Server-Protocol': 'HTTP/1.1',
'Custom-Header': 'blah',
@@ -102,6 +104,46 @@ class TestPHPApplication(TestApplicationPHP):
self.assertEqual(resp['status'], 200, 'status')
self.assertNotEqual(resp['body'], '', 'body not empty')
+ def test_php_application_header_status(self):
+ self.load('header')
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'X-Header': 'HTTP/1.1 404 Not Found',
+ }
+ )['status'],
+ 404,
+ 'status',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'X-Header': 'http/1.1 404 Not Found',
+ }
+ )['status'],
+ 404,
+ 'status case insensitive',
+ )
+
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Connection': 'close',
+ 'X-Header': 'HTTP/ 404 Not Found',
+ }
+ )['status'],
+ 404,
+ 'status version empty',
+ )
+
+
def test_php_application_404(self):
self.load('404')
@@ -420,6 +462,45 @@ class TestPHPApplication(TestApplicationPHP):
self.get()['body'], r'012345', 'disable_classes before'
)
+ def test_php_application_script(self):
+ self.assertIn(
+ 'success', self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/script"}},
+ "applications": {
+ "script": {
+ "type": "php",
+ "processes": {"spare": 0},
+ "root": self.current_dir + "/php/script",
+ "script": "phpinfo.php",
+ }
+ },
+ }
+ ), 'configure script'
+ )
+
+ resp = self.get()
+
+ self.assertEqual(resp['status'], 200, 'status')
+ self.assertNotEqual(resp['body'], '', 'body not empty')
+
+ def test_php_application_index_default(self):
+ self.assertIn(
+ 'success', self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/phpinfo"}},
+ "applications": {
+ "phpinfo": {
+ "type": "php",
+ "processes": {"spare": 0},
+ "root": self.current_dir + "/php/phpinfo",
+ }
+ },
+ }
+ ), 'configure index default'
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'status')
if __name__ == '__main__':
TestPHPApplication.main()
diff --git a/test/test_php_basic.py b/test/test_php_basic.py
index 02ff81de..0c84f206 100644
--- a/test/test_php_basic.py
+++ b/test/test_php_basic.py
@@ -164,6 +164,32 @@ class TestPHPBasic(TestControl):
'error', self.conf_delete('applications/app'), 'delete app again'
)
+ def test_php_delete_blocks(self):
+ self.conf(self.conf_basic)
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('listeners'),
+ 'listeners delete',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('applications'),
+ 'applications delete',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf(self.conf_app, 'applications'),
+ 'listeners restore',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners'),
+ 'applications restore',
+ )
if __name__ == '__main__':
TestPHPBasic.main()
diff --git a/test/test_python_basic.py b/test/test_python_basic.py
index 9987e886..e63158e5 100644
--- a/test/test_python_basic.py
+++ b/test/test_python_basic.py
@@ -177,6 +177,33 @@ class TestPythonBasic(TestControl):
'error', self.conf_delete('applications/app'), 'delete app again'
)
+ def test_python_delete_blocks(self):
+ self.conf(self.conf_basic)
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('listeners'),
+ 'listeners delete',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('applications'),
+ 'applications delete',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf(self.conf_app, 'applications'),
+ 'listeners restore',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners'),
+ 'applications restore',
+ )
+
if __name__ == '__main__':
TestPythonBasic.main()
diff --git a/test/test_routing.py b/test/test_routing.py
index ac2e0de8..6073877d 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -38,6 +38,9 @@ class TestRouting(TestApplicationProto):
}
)
+ def route(self, route):
+ return self.conf([route], 'routes')
+
def test_routes_match_method_positive(self):
self.assertEqual(self.get()['status'], 200, 'method positive GET')
self.assertEqual(self.post()['status'], 404, 'method positive POST')
@@ -45,14 +48,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_method_positive_many(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": ["GET", "POST"]},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": ["GET", "POST"]},
+ "action": {"pass": "applications/empty"},
+ }
),
'method positive many configure',
)
@@ -68,14 +68,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_method_negative(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": "!GET"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "!GET"},
+ "action": {"pass": "applications/empty"},
+ }
),
'method negative configure',
)
@@ -86,14 +83,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_method_negative_many(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": ["!GET", "!POST"]},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": ["!GET", "!POST"]},
+ "action": {"pass": "applications/empty"},
+ }
),
'method negative many configure',
)
@@ -109,14 +103,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_method_wildcard_left(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": "*ET"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "*ET"},
+ "action": {"pass": "applications/empty"},
+ }
),
'method wildcard left configure',
)
@@ -129,14 +120,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_method_wildcard_right(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": "GE*"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "GE*"},
+ "action": {"pass": "applications/empty"},
+ }
),
'method wildcard right configure',
)
@@ -151,14 +139,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_method_wildcard_left_right(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": "*GET*"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "*GET*"},
+ "action": {"pass": "applications/empty"},
+ }
),
'method wildcard left right configure',
)
@@ -173,14 +158,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_method_wildcard(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": "*"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "*"},
+ "action": {"pass": "applications/empty"},
+ }
),
'method wildcard configure',
)
@@ -190,70 +172,55 @@ class TestRouting(TestApplicationProto):
def test_routes_match_invalid(self):
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {"method": "**"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "**"},
+ "action": {"pass": "applications/empty"},
+ }
),
'wildcard invalid',
)
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {"method": "blah**"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "blah**"},
+ "action": {"pass": "applications/empty"},
+ }
),
'wildcard invalid 2',
)
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {"host": "*blah*blah"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": "*blah*blah"},
+ "action": {"pass": "applications/empty"},
+ }
),
'wildcard invalid 3',
)
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {"host": "blah*blah*blah"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": "blah*blah*blah"},
+ "action": {"pass": "applications/empty"},
+ }
),
'wildcard invalid 4',
)
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {"host": "blah*blah*"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": "blah*blah*"},
+ "action": {"pass": "applications/empty"},
+ }
),
'wildcard invalid 5',
)
@@ -261,14 +228,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_wildcard_middle(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"host": "ex*le"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": "ex*le"},
+ "action": {"pass": "applications/empty"},
+ }
),
'host wildcard middle configure',
)
@@ -308,14 +272,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_method_case_insensitive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": "get"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "get"},
+ "action": {"pass": "applications/empty"},
+ }
),
'method case insensitive configure',
)
@@ -325,14 +286,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_wildcard_left_case_insensitive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": "*et"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "*et"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match wildcard case insensitive configure',
)
@@ -344,14 +302,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_wildcard_middle_case_insensitive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": "g*t"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "g*t"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match wildcard case insensitive configure',
)
@@ -363,14 +318,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_wildcard_right_case_insensitive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": "get*"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "get*"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match wildcard case insensitive configure',
)
@@ -382,14 +334,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_wildcard_substring_case_insensitive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": "*et*"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "*et*"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match wildcard substring case insensitive configure',
)
@@ -403,14 +352,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_wildcard_left_case_sensitive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"uri": "*blah"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"uri": "*blah"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match wildcard left case sensitive configure',
)
@@ -430,14 +376,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_wildcard_middle_case_sensitive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"uri": "/b*h"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"uri": "/b*h"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match wildcard middle case sensitive configure',
)
@@ -457,14 +400,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_wildcard_right_case_sensitive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"uri": "/bla*"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"uri": "/bla*"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match wildcard right case sensitive configure',
)
@@ -484,14 +424,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_wildcard_substring_case_sensitive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"uri": "*bla*"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"uri": "*bla*"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match wildcard substring case sensitive configure',
)
@@ -677,14 +614,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_host_positive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"host": "localhost"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": "localhost"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match host positive configure',
)
@@ -729,14 +663,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_host_absent(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"host": "localhost"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": "localhost"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match host absent configure',
)
@@ -750,14 +681,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_host_ipv4(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"host": "127.0.0.1"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": "127.0.0.1"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match host ipv4 configure',
)
@@ -773,14 +701,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_host_ipv6(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"host": "[::1]"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": "[::1]"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match host ipv6 configure',
)
@@ -804,14 +729,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_host_positive_many(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"host": ["localhost", "example.com"]},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": ["localhost", "example.com"]},
+ "action": {"pass": "applications/empty"},
+ }
),
'match host positive many configure',
)
@@ -831,16 +753,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_host_positive_and_negative(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "host": ["*example.com", "!www.example.com"]
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": ["*example.com", "!www.example.com"]},
+ "action": {"pass": "applications/empty"},
+ }
),
'match host positive and negative configure',
)
@@ -878,14 +795,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_host_positive_and_negative_wildcard(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"host": ["*example*", "!www.example*"]},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": ["*example*", "!www.example*"]},
+ "action": {"pass": "applications/empty"},
+ }
),
'match host positive and negative wildcard configure',
)
@@ -909,14 +823,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_host_case_insensitive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"host": "Example.com"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": "Example.com"},
+ "action": {"pass": "applications/empty"},
+ }
),
'host case insensitive configure',
)
@@ -940,14 +851,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_host_port(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"host": "example.com"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": "example.com"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match host port configure',
)
@@ -963,14 +871,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_host_empty(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"host": ""},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"host": ""},
+ "action": {"pass": "applications/empty"},
+ }
),
'match host empty configure',
)
@@ -990,14 +895,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_uri_positive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"uri": "/"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"uri": "/"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match uri positive configure',
)
@@ -1025,14 +927,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_uri_case_sensitive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"uri": "/BLAH"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"uri": "/BLAH"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match uri case sensitive configure',
)
@@ -1056,14 +955,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_uri_normalize(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"uri": "/blah"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"uri": "/blah"},
+ "action": {"pass": "applications/empty"},
+ }
),
'match uri normalize configure',
)
@@ -1075,14 +971,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_empty_array(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"uri": []},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"uri": []},
+ "action": {"pass": "applications/empty"},
+ }
),
'match empty array configure',
)
@@ -1180,14 +1073,11 @@ class TestRouting(TestApplicationProto):
def test_routes_edit(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": "GET"},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": "GET"},
+ "action": {"pass": "applications/empty"},
+ }
),
'routes edit configure',
)
@@ -1324,14 +1214,11 @@ class TestRouting(TestApplicationProto):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"method": ["GET", "POST"]},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"method": ["GET", "POST"]},
+ "action": {"pass": "applications/empty"},
+ }
),
'match edit configure',
)
@@ -1457,18 +1344,15 @@ class TestRouting(TestApplicationProto):
def test_routes_match_rules(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "method": "GET",
- "host": "localhost",
- "uri": "/",
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {
+ "method": "GET",
+ "host": "localhost",
+ "uri": "/",
+ },
+ "action": {"pass": "applications/empty"},
+ }
),
'routes match rules configure',
)
@@ -1478,10 +1362,7 @@ class TestRouting(TestApplicationProto):
def test_routes_loop(self):
self.assertIn(
'success',
- self.conf(
- [{"match": {"uri": "/"}, "action": {"pass": "routes"}}],
- 'routes',
- ),
+ self.route({"match": {"uri": "/"}, "action": {"pass": "routes"}}),
'routes loop configure',
)
@@ -1490,14 +1371,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_headers(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"headers": {"host": "localhost"}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"headers": {"host": "localhost"}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers configure',
)
@@ -1547,16 +1425,13 @@ class TestRouting(TestApplicationProto):
def test_routes_match_headers_multiple(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "headers": {"host": "localhost", "x-blah": "test"}
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {
+ "headers": {"host": "localhost", "x-blah": "test"}
+ },
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers multiple configure',
)
@@ -1590,14 +1465,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_headers_multiple_values(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"headers": {"x-blah": "test"}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"headers": {"x-blah": "test"}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers multiple values configure',
)
@@ -1639,14 +1511,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_headers_multiple_rules(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"headers": {"x-blah": ["test", "blah"]}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"headers": {"x-blah": ["test", "blah"]}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers multiple rules configure',
)
@@ -1706,14 +1575,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_headers_case_insensitive(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"headers": {"X-BLAH": "TEST"}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"headers": {"X-BLAH": "TEST"}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers case insensitive configure',
)
@@ -1733,28 +1599,22 @@ class TestRouting(TestApplicationProto):
def test_routes_match_headers_invalid(self):
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {"headers": ["blah"]},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"headers": ["blah"]},
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers invalid',
)
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {"headers": {"foo": ["bar", {}]}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"headers": {"foo": ["bar", {}]}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers invalid 2',
)
@@ -1762,14 +1622,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_headers_empty_rule(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"headers": {"host": ""}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"headers": {"host": ""}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers empty rule configure',
)
@@ -1785,14 +1642,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_headers_rule_field_empty(self):
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {"headers": {"": "blah"}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"headers": {"": "blah"}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers rule field empty configure',
)
@@ -1800,14 +1654,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_headers_empty(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"headers": {}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"headers": {}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers empty configure',
)
@@ -1816,14 +1667,11 @@ class TestRouting(TestApplicationProto):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"headers": []},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"headers": []},
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers array empty configure 2',
)
@@ -1835,14 +1683,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_headers_rule_array_empty(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"headers": {"blah": []}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"headers": {"blah": []}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers rule array empty configure',
)
@@ -1863,21 +1708,18 @@ class TestRouting(TestApplicationProto):
def test_routes_match_headers_array(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "headers": [
- {"x-header1": "foo*"},
- {"x-header2": "bar"},
- {"x-header3": ["foo", "bar"]},
- {"x-header1": "bar", "x-header4": "foo"},
- ]
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {
+ "headers": [
+ {"x-header1": "foo*"},
+ {"x-header2": "bar"},
+ {"x-header3": ["foo", "bar"]},
+ {"x-header1": "bar", "x-header4": "foo"},
+ ]
+ },
+ "action": {"pass": "applications/empty"},
+ }
),
'match headers array configure',
)
@@ -1972,14 +1814,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_arguments(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"arguments": {"foo": "bar"}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": {"foo": "bar"}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments configure',
)
@@ -1993,12 +1832,12 @@ class TestRouting(TestApplicationProto):
self.get(url='/?Foo=bar')['status'],
404,
'match arguments case sensitive',
- ) # FAIL
+ )
self.assertEqual(
self.get(url='/?foo=Bar')['status'],
404,
'match arguments case sensitive 2',
- ) # FAIL
+ )
self.assertEqual(
self.get(url='/?foo=bar1')['status'],
404,
@@ -2013,14 +1852,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_arguments_empty(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"arguments": {}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": {}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments empty configure',
)
@@ -2029,14 +1865,11 @@ class TestRouting(TestApplicationProto):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"arguments": []},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": []},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments empty configure 2',
)
@@ -2046,46 +1879,33 @@ class TestRouting(TestApplicationProto):
def test_routes_match_arguments_invalid(self):
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {"arguments": ["var"]},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": ["var"]},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments invalid',
)
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {"arguments": [{"var1": {}}]},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": [{"var1": {}}]},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments invalid 2',
)
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {
- "arguments": {
- "": "bar"
- }
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": {"": "bar"}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments invalid 3',
)
@@ -2094,18 +1914,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_arguments_space(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "arguments": {
- "foo": "bar "
- }
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": {"foo": "bar "}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments space configure',
)
@@ -2130,18 +1943,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_arguments_plus(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "arguments": [
- {"foo": "bar+"}
- ]
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": [{"foo": "bar+"}]},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments plus configure',
)
@@ -2161,18 +1967,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_arguments_hex(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "arguments": [
- {"foo": "bar"}
- ]
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": [{"foo": "bar"}]},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments hex configure',
)
@@ -2186,18 +1985,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_arguments_chars(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "arguments": {
- "foo": "-._()[],;"
- }
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": {"foo": "-._()[],;"}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments chars configure',
)
@@ -2211,18 +2003,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_arguments_complex(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "arguments": {
- "foo": ""
- }
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": {"foo": ""}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments complex configure',
)
@@ -2266,16 +2051,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_arguments_multiple(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "arguments": {"foo": "bar", "blah": "test"}
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": {"foo": "bar", "blah": "test"}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments multiple configure',
)
@@ -2297,14 +2077,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_arguments_multiple_rules(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"arguments": {"foo": ["bar", "blah"]}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"arguments": {"foo": ["bar", "blah"]}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments multiple rules configure',
)
@@ -2340,21 +2117,18 @@ class TestRouting(TestApplicationProto):
def test_routes_match_arguments_array(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "arguments": [
- {"var1": "val1*"},
- {"var2": "val2"},
- {"var3": ["foo", "bar"]},
- {"var1": "bar", "var4": "foo"},
- ]
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {
+ "arguments": [
+ {"var1": "val1*"},
+ {"var2": "val2"},
+ {"var3": ["foo", "bar"]},
+ {"var1": "bar", "var4": "foo"},
+ ]
+ },
+ "action": {"pass": "applications/empty"},
+ }
),
'match arguments array configure',
)
@@ -2406,14 +2180,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_cookies(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"cookies": {"foO": "bar"}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"cookies": {"foO": "bar"}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match cookie configure',
)
@@ -2423,7 +2194,7 @@ class TestRouting(TestApplicationProto):
self.get(
headers={
'Host': 'localhost',
- 'Cookie': 'foo=bar',
+ 'Cookie': 'foO=bar',
'Connection': 'close',
},
)['status'],
@@ -2434,7 +2205,7 @@ class TestRouting(TestApplicationProto):
self.get(
headers={
'Host': 'localhost',
- 'Cookie': ['foo=bar', 'blah=blah'],
+ 'Cookie': ['foO=bar', 'blah=blah'],
'Connection': 'close',
},
)['status'],
@@ -2445,7 +2216,7 @@ class TestRouting(TestApplicationProto):
self.get(
headers={
'Host': 'localhost',
- 'Cookie': 'foo=bar; blah=blah',
+ 'Cookie': 'foO=bar; blah=blah',
'Connection': 'close',
},
)['status'],
@@ -2461,25 +2232,25 @@ class TestRouting(TestApplicationProto):
'Connection': 'close',
},
)['status'],
- 200,
- 'match cookies case insensitive',
+ 404,
+ 'match cookies case sensitive',
)
self.assertEqual(
self.get(
headers={
'Host': 'localhost',
- 'Cookie': 'foo=Bar',
+ 'Cookie': 'foO=Bar',
'Connection': 'close',
},
)['status'],
- 200,
- 'match cookies case insensitive 2',
+ 404,
+ 'match cookies case sensitive 2',
)
self.assertEqual(
self.get(
headers={
'Host': 'localhost',
- 'Cookie': 'foo=bar1',
+ 'Cookie': 'foO=bar1',
'Connection': 'close',
},
)['status'],
@@ -2490,25 +2261,33 @@ class TestRouting(TestApplicationProto):
self.get(
headers={
'Host': 'localhost',
- 'Cookie': 'foo=bar;',
+ 'Cookie': '1foO=bar;',
'Connection': 'close',
},
)['status'],
- 200,
+ 404,
'match cookies exact 2',
)
+ self.assertEqual(
+ self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Cookie': 'foO=bar;1',
+ 'Connection': 'close',
+ },
+ )['status'],
+ 200,
+ 'match cookies exact 3',
+ )
def test_routes_match_cookies_empty(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"cookies": {}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"cookies": {}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match cookies empty configure',
)
@@ -2517,14 +2296,11 @@ class TestRouting(TestApplicationProto):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"cookies": []},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"cookies": []},
+ "action": {"pass": "applications/empty"},
+ }
),
'match cookies empty configure 2',
)
@@ -2534,28 +2310,22 @@ class TestRouting(TestApplicationProto):
def test_routes_match_cookies_invalid(self):
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {"cookies": ["var"]},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"cookies": ["var"]},
+ "action": {"pass": "applications/empty"},
+ }
),
'match cookies invalid',
)
self.assertIn(
'error',
- self.conf(
- [
- {
- "match": {"cookies": [{"foo": {}}]},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"cookies": [{"foo": {}}]},
+ "action": {"pass": "applications/empty"},
+ }
),
'match cookies invalid 2',
)
@@ -2563,16 +2333,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_cookies_multiple(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "cookies": {"foo": "bar", "blah": "blah"}
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"cookies": {"foo": "bar", "blah": "blah"}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match cookies multiple configure',
)
@@ -2630,14 +2395,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_cookies_multiple_values(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"cookies": {"blah": "blah"}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"cookies": {"blah": "blah"}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match cookies multiple values configure',
)
@@ -2679,14 +2441,11 @@ class TestRouting(TestApplicationProto):
def test_routes_match_cookies_multiple_rules(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {"cookies": {"blah": ["test", "blah"]}},
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {"cookies": {"blah": ["test", "blah"]}},
+ "action": {"pass": "applications/empty"},
+ }
),
'match cookies multiple rules configure',
)
@@ -2758,21 +2517,18 @@ class TestRouting(TestApplicationProto):
def test_routes_match_cookies_array(self):
self.assertIn(
'success',
- self.conf(
- [
- {
- "match": {
- "cookies": [
- {"var1": "val1*"},
- {"var2": "val2"},
- {"var3": ["foo", "bar"]},
- {"var1": "bar", "var4": "foo"},
- ]
- },
- "action": {"pass": "applications/empty"},
- }
- ],
- 'routes',
+ self.route(
+ {
+ "match": {
+ "cookies": [
+ {"var1": "val1*"},
+ {"var2": "val2"},
+ {"var3": ["foo", "bar"]},
+ {"var1": "bar", "var4": "foo"},
+ ]
+ },
+ "action": {"pass": "applications/empty"},
+ }
),
'match cookies array configure',
)
@@ -2885,5 +2641,99 @@ class TestRouting(TestApplicationProto):
'match cookies array 10',
)
+ def test_routes_match_scheme(self):
+ self.assertIn(
+ 'success',
+ self.route(
+ {
+ "match": {"scheme": "http"},
+ "action": {"pass": "applications/empty"},
+ }
+ ),
+ 'match scheme http configure',
+ )
+ self.assertIn(
+ 'success',
+ self.route(
+ {
+ "match": {"scheme": "https"},
+ "action": {"pass": "applications/empty"},
+ }
+ ),
+ 'match scheme https configure',
+ )
+ self.assertIn(
+ 'success',
+ self.route(
+ {
+ "match": {"scheme": "HtTp"},
+ "action": {"pass": "applications/empty"},
+ }
+ ),
+ 'match scheme http case insensitive configure',
+ )
+ self.assertIn(
+ 'success',
+ self.route(
+ {
+ "match": {"scheme": "HtTpS"},
+ "action": {"pass": "applications/empty"},
+ }
+ ),
+ 'match scheme https case insensitive configure',
+ )
+
+ def test_routes_match_scheme_invalid(self):
+ self.assertIn(
+ 'error',
+ self.route(
+ {
+ "match": {"scheme": ["http"]},
+ "action": {"pass": "applications/empty"},
+ }
+ ),
+ 'scheme invalid type no arrays allowed',
+ )
+ self.assertIn(
+ 'error',
+ self.route(
+ {
+ "match": {"scheme": "ftp"},
+ "action": {"pass": "applications/empty"},
+ }
+ ),
+ 'scheme invalid protocol 1',
+ )
+ self.assertIn(
+ 'error',
+ self.route(
+ {
+ "match": {"scheme": "ws"},
+ "action": {"pass": "applications/empty"},
+ }
+ ),
+ 'scheme invalid protocol 2',
+ )
+ self.assertIn(
+ 'error',
+ self.route(
+ {
+ "match": {"scheme": "*"},
+ "action": {"pass": "applications/empty"},
+ }
+ ),
+ 'scheme invalid no wildcard allowed',
+ )
+ self.assertIn(
+ 'error',
+ self.route(
+ {
+ "match": {"scheme": ""},
+ "action": {"pass": "applications/empty"},
+ }
+ ),
+ 'scheme invalid empty',
+ )
+
if __name__ == '__main__':
TestRouting.main()
diff --git a/test/test_routing_tls.py b/test/test_routing_tls.py
new file mode 100644
index 00000000..433a303e
--- /dev/null
+++ b/test/test_routing_tls.py
@@ -0,0 +1,58 @@
+from unit.applications.tls import TestApplicationTLS
+
+
+class TestRoutingTLS(TestApplicationTLS):
+ prerequisites = ['python', 'openssl']
+
+ def test_routes_match_scheme(self):
+ self.certificate()
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": "routes"},
+ "*:7081": {
+ "pass": "routes",
+ "tls": {"certificate": 'default'},
+ },
+ },
+ "routes": [
+ {
+ "match": {"scheme": "http"},
+ "action": {"pass": "applications/empty"},
+ },
+ {
+ "match": {"scheme": "https"},
+ "action": {"pass": "applications/204_no_content"},
+ },
+ ],
+ "applications": {
+ "empty": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": self.current_dir + "/python/empty",
+ "module": "wsgi",
+ },
+ "204_no_content": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": self.current_dir
+ + "/python/204_no_content",
+ "module": "wsgi",
+ },
+ },
+ }
+ ),
+ 'scheme configure',
+ )
+
+ self.assertEqual(self.get()['status'], 200, 'scheme http')
+ self.assertEqual(
+ self.get_ssl(port=7081)['status'], 204, 'scheme https'
+ )
+
+
+if __name__ == '__main__':
+ TestRoutingTLS.main()
diff --git a/test/test_tls.py b/test/test_tls.py
index 14efb3a7..076a2c38 100644
--- a/test/test_tls.py
+++ b/test/test_tls.py
@@ -1,6 +1,5 @@
import re
import ssl
-import time
import subprocess
import unittest
from unit.applications.tls import TestApplicationTLS
@@ -146,6 +145,8 @@ class TestTLS(TestApplicationTLS):
def test_tls_certificate_key_ec(self):
self.load('empty')
+ self.openssl_conf()
+
subprocess.call(
[
'openssl',
@@ -515,8 +516,6 @@ basicConstraints = critical,CA:TRUE"""
self.skip_alerts.append(r'process \d+ exited on signal 9')
self.load('mirror')
- self.assertEqual(self.get()['status'], 200, 'init')
-
self.certificate()
self.conf('1', 'applications/mirror/processes')
diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py
index c4390f15..ec1c95d9 100644
--- a/test/unit/applications/lang/java.py
+++ b/test/unit/applications/lang/java.py
@@ -64,7 +64,7 @@ class TestApplicationJava(TestApplicationProto):
"applications": {
script: {
"unit_jars": self.pardir + '/build',
- "type": "java",
+ "type": 'java',
"processes": {"spare": 0},
"working_directory": script_path,
"webapp": app_path,
diff --git a/test/unit/applications/lang/perl.py b/test/unit/applications/lang/perl.py
index 8aaf33a4..79df2cfa 100644
--- a/test/unit/applications/lang/perl.py
+++ b/test/unit/applications/lang/perl.py
@@ -2,6 +2,8 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationPerl(TestApplicationProto):
+ application_type = "perl"
+
def load(self, script, name='psgi.pl'):
script_path = self.current_dir + '/perl/' + script
@@ -10,7 +12,7 @@ class TestApplicationPerl(TestApplicationProto):
"listeners": {"*:7080": {"pass": "applications/" + script}},
"applications": {
script: {
- "type": "perl",
+ "type": self.application_type,
"processes": {"spare": 0},
"working_directory": script_path,
"script": script_path + '/' + name,
diff --git a/test/unit/applications/lang/php.py b/test/unit/applications/lang/php.py
index 99d84164..9c54368d 100644
--- a/test/unit/applications/lang/php.py
+++ b/test/unit/applications/lang/php.py
@@ -2,6 +2,8 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationPHP(TestApplicationProto):
+ application_type = "php"
+
def load(self, script, name='index.php'):
script_path = self.current_dir + '/php/' + script
@@ -10,7 +12,7 @@ class TestApplicationPHP(TestApplicationProto):
"listeners": {"*:7080": {"pass": "applications/" + script}},
"applications": {
script: {
- "type": "php",
+ "type": self.application_type,
"processes": {"spare": 0},
"root": script_path,
"working_directory": script_path,
diff --git a/test/unit/applications/lang/python.py b/test/unit/applications/lang/python.py
index d1b5b839..ded76cb6 100644
--- a/test/unit/applications/lang/python.py
+++ b/test/unit/applications/lang/python.py
@@ -2,6 +2,8 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationPython(TestApplicationProto):
+ application_type = "python"
+
def load(self, script, name=None):
if name is None:
name = script
@@ -13,7 +15,7 @@ class TestApplicationPython(TestApplicationProto):
"listeners": {"*:7080": {"pass": "applications/" + name}},
"applications": {
name: {
- "type": "python",
+ "type": self.application_type,
"processes": {"spare": 0},
"path": script_path,
"working_directory": script_path,
diff --git a/test/unit/applications/lang/ruby.py b/test/unit/applications/lang/ruby.py
index c2d8633e..d30735ad 100644
--- a/test/unit/applications/lang/ruby.py
+++ b/test/unit/applications/lang/ruby.py
@@ -2,6 +2,8 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationRuby(TestApplicationProto):
+ application_type = "ruby"
+
def load(self, script, name='config.ru'):
script_path = self.current_dir + '/ruby/' + script
@@ -10,7 +12,7 @@ class TestApplicationRuby(TestApplicationProto):
"listeners": {"*:7080": {"pass": "applications/" + script}},
"applications": {
script: {
- "type": "ruby",
+ "type": self.application_type,
"processes": {"spare": 0},
"working_directory": script_path,
"script": script_path + '/' + name,
diff --git a/test/unit/applications/tls.py b/test/unit/applications/tls.py
index c8287ac5..6e8deefb 100644
--- a/test/unit/applications/tls.py
+++ b/test/unit/applications/tls.py
@@ -1,3 +1,4 @@
+import os
import ssl
import subprocess
from unit.applications.proto import TestApplicationProto
@@ -12,6 +13,8 @@ class TestApplicationTLS(TestApplicationProto):
self.context.verify_mode = ssl.CERT_NONE
def certificate(self, name='default', load=True):
+ self.openssl_conf()
+
subprocess.call(
[
'openssl',
@@ -59,13 +62,13 @@ class TestApplicationTLS(TestApplicationProto):
return ssl.get_server_certificate(addr, ssl_version=ssl_version)
- def load(self, script, name=None):
- if name is None:
- name = script
+ def openssl_conf(self):
+ conf_path = self.testdir + '/openssl.conf'
- # create default openssl configuration
+ if os.path.exists(conf_path):
+ return
- with open(self.testdir + '/openssl.conf', 'w') as f:
+ with open(conf_path, 'w') as f:
f.write(
"""[ req ]
default_bits = 2048
@@ -74,9 +77,13 @@ distinguished_name = req_distinguished_name
[ req_distinguished_name ]"""
)
+ def load(self, script, name=None):
+ if name is None:
+ name = script
+
script_path = self.current_dir + '/python/' + script
- self.conf(
+ self._load_conf(
{
"listeners": {"*:7080": {"pass": "applications/" + name}},
"applications": {
diff --git a/test/unit/applications/websockets.py b/test/unit/applications/websockets.py
new file mode 100644
index 00000000..417e9504
--- /dev/null
+++ b/test/unit/applications/websockets.py
@@ -0,0 +1,215 @@
+import random
+import base64
+import struct
+import select
+import hashlib
+import itertools
+from unit.applications.proto import TestApplicationProto
+
+GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+
+class TestApplicationWebsocket(TestApplicationProto):
+
+ OP_CONT = 0x00
+ OP_TEXT = 0x01
+ OP_BINARY = 0x02
+ OP_CLOSE = 0x08
+ OP_PING = 0x09
+ OP_PONG = 0x0A
+ CLOSE_CODES = [1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011]
+
+ def __init__(self, preinit=False):
+ self.preinit = preinit
+
+ def key(self):
+ raw_key = bytes(random.getrandbits(8) for _ in range(16))
+ return base64.b64encode(raw_key).decode()
+
+ def accept(self, key):
+ sha1 = hashlib.sha1((key + GUID).encode()).digest()
+ return base64.b64encode(sha1).decode()
+
+ def upgrade(self):
+ key = self.key()
+
+ if self.preinit:
+ self.get()
+
+ resp, sock = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13,
+ },
+ read_timeout=1,
+ start=True,
+ )
+
+ return (resp, sock, key)
+
+ def apply_mask(self, data, mask):
+ return bytes(b ^ m for b, m in zip(data, itertools.cycle(mask)))
+
+ def serialize_close(self, code = 1000, reason = ''):
+ return struct.pack('!H', code) + reason.encode('utf-8')
+
+ def frame_read(self, sock, read_timeout=10):
+ def recv_bytes(sock, bytes):
+ data = b''
+ while select.select([sock], [], [], read_timeout)[0]:
+ try:
+ if bytes < 65536:
+ data = sock.recv(bytes)
+ else:
+ data = self.recvall(
+ sock,
+ read_timeout=read_timeout,
+ buff_size=bytes,
+ )
+ break
+ except:
+ break
+
+ return data
+
+ frame = {}
+
+ head1, = struct.unpack('!B', recv_bytes(sock, 1))
+ head2, = struct.unpack('!B', recv_bytes(sock, 1))
+
+ frame['fin'] = bool(head1 & 0b10000000)
+ frame['rsv1'] = bool(head1 & 0b01000000)
+ frame['rsv2'] = bool(head1 & 0b00100000)
+ frame['rsv3'] = bool(head1 & 0b00010000)
+ frame['opcode'] = head1 & 0b00001111
+ frame['mask'] = head2 & 0b10000000
+
+ length = head2 & 0b01111111
+ if length == 126:
+ data = recv_bytes(sock, 2)
+ length, = struct.unpack('!H', data)
+ elif length == 127:
+ data = recv_bytes(sock, 8)
+ length, = struct.unpack('!Q', data)
+
+ if frame['mask']:
+ mask_bits = recv_bytes(sock, 4)
+
+ data = recv_bytes(sock, length)
+ if frame['mask']:
+ data = self.apply_mask(data, mask_bits)
+
+ if frame['opcode'] == self.OP_CLOSE:
+ if length >= 2:
+ code, = struct.unpack('!H', data[:2])
+ reason = data[2:].decode('utf-8')
+ if not (code in self.CLOSE_CODES or 3000 <= code < 5000):
+ self.fail('Invalid status code')
+ frame['code'] = code
+ frame['reason'] = reason
+ elif length == 0:
+ frame['code'] = 1005
+ frame['reason'] = ''
+ else:
+ self.fail('Close frame too short')
+
+ frame['data'] = data
+
+ if frame['mask']:
+ self.fail('Received frame with mask')
+
+ return frame
+
+ def frame_to_send(
+ self,
+ opcode,
+ data,
+ fin=True,
+ length=None,
+ rsv1=False,
+ rsv2=False,
+ rsv3=False,
+ mask=True,
+ ):
+ frame = b''
+
+ if isinstance(data, str):
+ data = data.encode('utf-8')
+
+ head1 = (
+ (0b10000000 if fin else 0)
+ | (0b01000000 if rsv1 else 0)
+ | (0b00100000 if rsv2 else 0)
+ | (0b00010000 if rsv3 else 0)
+ | opcode
+ )
+
+ head2 = 0b10000000 if mask else 0
+
+ data_length = len(data) if length is None else length
+ if data_length < 126:
+ frame += struct.pack('!BB', head1, head2 | data_length)
+ elif data_length < 65536:
+ frame += struct.pack('!BBH', head1, head2 | 126, data_length)
+ else:
+ frame += struct.pack('!BBQ', head1, head2 | 127, data_length)
+
+ if mask:
+ mask_bits = struct.pack('!I', random.getrandbits(32))
+ frame += mask_bits
+
+ if mask:
+ frame += self.apply_mask(data, mask_bits)
+ else:
+ frame += data
+
+ return frame
+
+ def frame_write(self, sock, *args, **kwargs):
+ chopsize = kwargs.pop('chopsize') if 'chopsize' in kwargs else None
+
+ frame = self.frame_to_send(*args, **kwargs)
+
+ if chopsize is None:
+ sock.sendall(frame)
+
+ else:
+ pos = 0
+ frame_len = len(frame)
+ while (pos < frame_len):
+ end = min(pos + chopsize, frame_len)
+ sock.sendall(frame[pos:end])
+ pos = end
+
+ def message(self, sock, type, message, fragmention_size=None, **kwargs):
+ message_len = len(message)
+
+ if fragmention_size is None:
+ fragmention_size = message_len
+
+ if message_len <= fragmention_size:
+ self.frame_write(sock, type, message, **kwargs)
+ return
+
+ pos = 0
+ op_code = type
+ while(pos < message_len):
+ end = min(pos + fragmention_size, message_len)
+ fin = (end == message_len)
+ self.frame_write(sock, op_code, message[pos:end], fin=fin, **kwargs)
+ op_code = self.OP_CONT
+ pos = end
+
+ def message_read(self, sock, read_timeout=10):
+ frame = self.frame_read(sock, read_timeout=read_timeout)
+
+ while(not frame['fin']):
+ temp = self.frame_read(sock, read_timeout=read_timeout)
+ frame['data'] += temp['data']
+ frame['fin'] = temp['fin']
+
+ return frame
diff --git a/test/unit/http.py b/test/unit/http.py
index 1ce86e5a..c0af8a9e 100644
--- a/test/unit/http.py
+++ b/test/unit/http.py
@@ -81,7 +81,11 @@ class TestHTTP(TestUnit):
sock.sendall(req)
if TestUnit.detailed:
- print('>>>', req, sep='\n')
+ print('>>>')
+ try:
+ print(req.decode('utf-8', 'ignore'))
+ except UnicodeEncodeError:
+ print(req)
resp = ''
@@ -93,7 +97,11 @@ class TestHTTP(TestUnit):
resp = self.recvall(sock, read_timeout=read_timeout).decode(enc)
if TestUnit.detailed:
- print('<<<', resp.encode('utf-8'), sep='\n')
+ print('<<<')
+ try:
+ print(resp)
+ except UnicodeEncodeError:
+ print(resp.encode())
if 'raw_resp' not in kwargs:
resp = self._resp_to_dict(resp)
diff --git a/test/unit/main.py b/test/unit/main.py
index 49806fe7..6a167a9e 100644
--- a/test/unit/main.py
+++ b/test/unit/main.py
@@ -12,6 +12,8 @@ import subprocess
from multiprocessing import Process
+available_modules = {}
+
class TestUnit(unittest.TestCase):
current_dir = os.path.abspath(
@@ -21,6 +23,7 @@ class TestUnit(unittest.TestCase):
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
)
architecture = platform.architecture()[0]
+ system = platform.system()
maxDiff = None
detailed = False
@@ -34,6 +37,17 @@ class TestUnit(unittest.TestCase):
TestUnit._set_args(args)
+ def run(self, result=None):
+ if not hasattr(self, 'application_type'):
+ return super().run(result)
+
+ type = self.application_type
+ for prerequisite in self.prerequisites:
+ if prerequisite in available_modules:
+ for version in available_modules[prerequisite]:
+ self.application_type = type + ' ' + version
+ super().run(result)
+
@classmethod
def main(cls):
args, rest = TestUnit._parse_args()
@@ -108,6 +122,16 @@ class TestUnit(unittest.TestCase):
self.stop()
exit("Unit is writing log too long")
+ # discover all available modules
+
+ global available_modules
+ available_modules = {}
+ for module in re.findall(r'module: ([a-zA-Z]+) (.*) ".*"$', log, re.M):
+ if module[0] not in available_modules:
+ available_modules[module[0]] = [module[1]]
+ else:
+ available_modules[module[0]].append(module[1])
+
missed_module = ''
for module in modules:
if module == 'go':
@@ -153,7 +177,8 @@ class TestUnit(unittest.TestCase):
m = None
else:
- m = re.search('module: ' + module, log)
+ if module not in available_modules:
+ m = None
if m is None:
missed_module = module
@@ -309,6 +334,13 @@ class TestUnit(unittest.TestCase):
action='store_true',
help='Save unit.log after the test execution',
)
+ parser.add_argument(
+ '-u',
+ '--unsafe',
+ dest='unsafe',
+ action='store_true',
+ help='Run unsafe tests',
+ )
return parser.parse_known_args()
@@ -316,6 +348,7 @@ class TestUnit(unittest.TestCase):
def _set_args(args):
TestUnit.detailed = args.detailed
TestUnit.save_log = args.save_log
+ TestUnit.unsafe = args.unsafe
if TestUnit.detailed:
fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0)