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 }