1 /* 2 Copyright (c) 2023-2024 Andrea Fontana 3 4 Permission is hereby granted, free of charge, to any person 5 obtaining a copy of this software and associated documentation 6 files (the "Software"), to deal in the Software without 7 restriction, including without limitation the rights to use, 8 copy, modify, merge, publish, distribute, sublicense, and/or sell 9 copies of the Software, and to permit persons to whom the 10 Software is furnished to do so, subject to the following 11 conditions: 12 13 The above copyright notice and this permission notice shall be 14 included in all copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 OTHER DEALINGS IN THE SOFTWARE. 24 */ 25 26 module app; 27 28 import serverino; 29 import serverino.tests; 30 31 import std; 32 33 mixin ServerinoTest; 34 35 @route!"/sleep" 36 @endpoint void sleep(Request r, Output o) 37 { 38 import core.thread; 39 Thread.sleep(1.dur!"seconds"); 40 o.addHeader("Content-type", "text/plain"); 41 o ~= "slept"; 42 } 43 44 @route!"/simple" 45 @endpoint void simple(Request r, Output o) 46 { 47 import core.thread; 48 o.addHeader("Content-type", "text/plain"); 49 o ~= "simple"; 50 } 51 52 @onServerInit 53 ServerinoConfig conf() 54 { 55 return ServerinoConfig 56 .create() 57 .setMaxRequestTime(1.dur!"seconds") 58 .setMaxRequestSize(2000) 59 .addListener("0.0.0.0", 8080) 60 .setWorkers(4); 61 } 62 63 void test() 64 { 65 // Testing minimal http/1.0 request 66 { 67 auto req = "GET /simple HTTP/1.0\r\n\r\n"; 68 69 auto sck = new TcpSocket(); 70 sck.connect(new InternetAddress("localhost", 8080)); 71 sck.send(req); 72 73 char[] buffer; 74 char[] data; 75 76 buffer.length = 4096; 77 while(true) 78 { 79 auto ln = sck.receive(buffer); 80 81 if (ln <= 0) break; 82 83 data ~= buffer[0..ln]; 84 } 85 assert(data == "HTTP/1.0 200 OK\r\nconnection: close\r\ncontent-type: text/plain\r\ncontent-length: 6\r\n\r\nsimple"); 86 } 87 88 // Testing pipeline 89 { 90 import core.thread; 91 92 __gshared bool done = false; 93 94 new Thread({ 95 while(!done) 96 { 97 auto req = "GET /simple HTTP/1.0\r\nx-test:blah\r\n\r\n"; 98 auto sck = new TcpSocket(); 99 sck.connect(new InternetAddress("localhost", 8080)); 100 sck.send(req); 101 102 char[] buffer; 103 char[] data; 104 105 buffer.length = 4096; 106 while(true) 107 { 108 auto ln = sck.receive(buffer); 109 110 if (ln <= 0) break; 111 112 data ~= buffer[0..ln]; 113 } 114 115 Thread.sleep(100.msecs); 116 } 117 }).start(); 118 119 { 120 auto req = "GET /simple HTTP/1.1\r\nhost:localhost\r\n\r\n"; 121 req ~= "GET /sleep HTTP/1.1\r\nhost:localhost\r\n\r\n"; 122 req ~= "GET /simple HTTP/1.0\r\nhost:localhost\r\n\r\n"; 123 124 auto sck = new TcpSocket(); 125 sck.connect(new InternetAddress("localhost", 8080)); 126 sck.send(req); 127 128 char[] buffer; 129 char[] data; 130 131 buffer.length = 4096; 132 while(true) 133 { 134 auto ln = sck.receive(buffer); 135 136 if (ln <= 0) break; 137 138 data ~= buffer[0..ln]; 139 } 140 done = true; 141 142 assert(data == "HTTP/1.1 200 OK\r\nconnection: keep-alive\r\ncontent-type: text/plain\r\ncontent-length: 6\r\n\r\nsimpleHTTP/1.1 200 OK\r\nconnection: keep-alive\r\ncontent-type: text/plain\r\ncontent-length: 5\r\n\r\nsleptHTTP/1.0 200 OK\r\nconnection: close\r\ncontent-type: text/plain\r\ncontent-length: 6\r\n\r\nsimple"); 143 } 144 145 146 147 148 } 149 150 // Testing partial sending 151 { 152 import core.thread; 153 154 { 155 auto req = "GET /simple HTTP/1.1\r\nhost:localhost\r\n\r\n"; 156 req ~= "GET /sleep HTTP/1.1\r\nhost:localhost\r\n\r\n"; 157 req ~= "GET /simple HTTP/1.0\r\nhost:localhost\r\n\r\n"; 158 159 auto sck = new TcpSocket(); 160 sck.connect(new InternetAddress("localhost", 8080)); 161 sck.send(req[0..5]); 162 Thread.sleep(10.msecs); 163 sck.send(req[5..50]); 164 Thread.sleep(10.msecs); 165 sck.send(req[50..$]); 166 167 168 char[] buffer; 169 char[] data; 170 171 buffer.length = 4096; 172 while(true) 173 { 174 auto ln = sck.receive(buffer); 175 176 if (ln <= 0) break; 177 178 data ~= buffer[0..ln]; 179 } 180 181 assert(data == "HTTP/1.1 200 OK\r\nconnection: keep-alive\r\ncontent-type: text/plain\r\ncontent-length: 6\r\n\r\nsimpleHTTP/1.1 200 OK\r\nconnection: keep-alive\r\ncontent-type: text/plain\r\ncontent-length: 5\r\n\r\nsleptHTTP/1.0 200 OK\r\nconnection: close\r\ncontent-type: text/plain\r\ncontent-length: 6\r\n\r\nsimple"); 182 } 183 184 } 185 }