1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit;
16
17 import java.io.CharArrayWriter;
18 import java.io.Closeable;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 import java.net.BindException;
23 import java.net.InetSocketAddress;
24 import java.net.ServerSocket;
25 import java.net.Socket;
26 import java.net.SocketException;
27 import java.nio.charset.Charset;
28 import java.nio.charset.StandardCharsets;
29 import java.util.ArrayList;
30 import java.util.List;
31
32 import org.apache.commons.io.IOUtils;
33 import org.apache.commons.lang3.StringUtils;
34
35
36
37
38
39
40
41
42 public class PrimitiveWebServer implements Closeable {
43
44 private final int port_;
45 private final String firstResponse_;
46 private final String otherResponse_;
47 private ServerSocket server_;
48 private Charset charset_ = StandardCharsets.ISO_8859_1;
49 private List<String> requests_ = new ArrayList<>();
50
51
52
53
54
55
56
57
58
59 public PrimitiveWebServer(final Charset charset, final String firstResponse, final String otherResponse)
60 throws Exception {
61 port_ = WebTestCase.PORT_PRIMITIVE_SERVER;
62 firstResponse_ = firstResponse;
63 otherResponse_ = otherResponse;
64 if (charset != null) {
65 charset_ = charset;
66 }
67
68 start();
69 }
70
71
72
73
74
75 private void start() throws Exception {
76 server_ = new ServerSocket();
77 server_.setReuseAddress(true);
78
79 final long maxWait = System.currentTimeMillis() + WebServerTestCase.BIND_TIMEOUT;
80
81 while (true) {
82 try {
83 server_.bind(new InetSocketAddress(port_));
84 break;
85 }
86 catch (final BindException e) {
87 if (System.currentTimeMillis() > maxWait) {
88 throw (BindException) new BindException("Port " + port_ + " is already in use").initCause(e);
89 }
90 Thread.sleep(200);
91 }
92 }
93
94 new Thread(new Runnable() {
95
96 @Override
97 public void run() {
98 boolean first = true;
99 try {
100 while (true) {
101 final Socket socket = server_.accept();
102 final InputStream in = socket.getInputStream();
103 final CharArrayWriter writer = new CharArrayWriter();
104
105 String requestString = writer.toString();
106 int i;
107
108 while ((i = in.read()) != -1) {
109 writer.append((char) i);
110 requestString = writer.toString();
111
112 if (i == '\n' && requestString.endsWith("\r\n\r\n")) {
113 break;
114 }
115 }
116 final int contentLenghtPos =
117 StringUtils.indexOfIgnoreCase(requestString, HttpHeader.CONTENT_LENGTH);
118 if (contentLenghtPos > -1) {
119 final int endPos = requestString.indexOf('\n', contentLenghtPos + 16);
120 final String toParse = requestString.substring(contentLenghtPos + 16, endPos);
121 final int contentLenght = Integer.parseInt(toParse.trim());
122
123 if (contentLenght > 0) {
124 final byte[] charArray = new byte[contentLenght];
125 IOUtils.read(in, charArray, 0, contentLenght);
126 requestString += new String(charArray);
127 }
128 }
129
130 final String response;
131 if (requestString.length() < 1
132 || requestString.contains("/favicon.ico")) {
133 response = "HTTP/1.1 404 Not Found\r\n"
134 + "Content-Length: 0\r\n"
135 + "Connection: close\r\n"
136 + "\r\n";
137 }
138 else {
139 requests_.add(requestString);
140 if (first || otherResponse_ == null) {
141 response = firstResponse_;
142 }
143 else {
144 response = otherResponse_;
145 }
146 first = false;
147 }
148
149 try (OutputStream out = socket.getOutputStream()) {
150 final int headPos = response.indexOf("\r\n\r\n");
151 out.write(response.substring(0, headPos + 4).getBytes(StandardCharsets.US_ASCII));
152 out.write(response.substring(headPos + 4).getBytes(charset_));
153 }
154 }
155 }
156 catch (final SocketException e) {
157
158 }
159 catch (final Exception e) {
160 throw new RuntimeException(e);
161 }
162 }
163 }).start();
164 }
165
166
167
168
169
170 @Override
171 public void close() throws IOException {
172 server_.close();
173 }
174
175
176
177
178
179 public List<String> getRequests() {
180 return requests_;
181 }
182
183
184
185
186
187 public int getPort() {
188 return port_;
189 }
190
191
192
193
194 public void clearRequests() {
195 requests_.clear();
196 }
197 }