• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / httpunit / 755

14 Feb 2026 07:14PM UTC coverage: 80.526%. Remained the same
755

push

github

hazendaz
[ci] Fix badge

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10124 relevant lines covered (81.44%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.78
/src/main/java/com/meterware/pseudoserver/PseudoServer.java
1
/*
2
 * SPDX-License-Identifier: MIT
3
 * See LICENSE file for details.
4
 *
5
 * Copyright 2000-2026 Russell Gold
6
 * Copyright 2021-2000 hazendaz
7
 */
8
package com.meterware.pseudoserver;
9

10
import com.meterware.httpunit.HttpUnitUtils;
11

12
import java.io.BufferedInputStream;
13
import java.io.IOException;
14
import java.io.InputStream;
15
import java.io.InterruptedIOException;
16
import java.io.OutputStream;
17
import java.io.OutputStreamWriter;
18
import java.io.PrintStream;
19
import java.io.PrintWriter;
20
import java.net.HttpURLConnection;
21
import java.net.ServerSocket;
22
import java.net.Socket;
23
import java.nio.charset.Charset;
24
import java.nio.file.Files;
25
import java.nio.file.Path;
26
import java.util.ArrayList;
27
import java.util.Collections;
28
import java.util.Enumeration;
29
import java.util.Hashtable;
30
import java.util.Iterator;
31
import java.util.List;
32
import java.util.StringTokenizer;
33

34
/**
35
 * A basic simulated web-server for testing user agents without a web server.
36
 **/
37
public class PseudoServer {
38

39
    /**
40
     * allow factory use to be switched on and off by default the factory is not used any more since there were problems
41
     * with the test cases as of 2012-10-09.
42
     */
43
    public static final boolean useFactory = false;
44

45
    /** The Constant DEFAULT_SOCKET_TIMEOUT. */
46
    static final int DEFAULT_SOCKET_TIMEOUT = 1000;
47

48
    /** The Constant INPUT_POLL_INTERVAL. */
49
    private static final int INPUT_POLL_INTERVAL = 10;
50

51
    /** Time in msec to wait for an outstanding server socket to be released before creating a new one. **/
52
    private static int _socketReleaseWaitTime = 50;
1✔
53

54
    /** Number of outstanding server sockets that must be present before trying to wait for one to be released. **/
55
    private static int _waitThreshhold = 10;
1✔
56

57
    /** The num servers. */
58
    private static int _numServers = 0;
1✔
59

60
    /** The server num. */
61
    private int _serverNum = 0;
1✔
62

63
    /** The connection num. */
64
    private int _connectionNum = 0;
1✔
65

66
    /** The classpath dirs. */
67
    private ArrayList _classpathDirs = new ArrayList<>();
1✔
68

69
    /** The max protocol level. */
70
    private String _maxProtocolLevel = "1.1";
1✔
71

72
    /** The socket timeout. */
73
    private final int _socketTimeout;
74

75
    /**
76
     * Returns the amount of time the pseudo server will wait for a server socket to be released (in msec) before
77
     * allocating a new one. See also {@link #getWaitThreshhold getWaitThreshhold}.
78
     *
79
     * @return the socket release wait time
80
     */
81
    public static int getSocketReleaseWaitTime() {
82
        return _socketReleaseWaitTime;
×
83
    }
84

85
    /**
86
     * Returns the amount of time the pseudo server will wait for a server socket to be released (in msec) before
87
     * allocating a new one. See also {@link #getWaitThreshhold getWaitThreshhold}.
88
     *
89
     * @param socketReleaseWaitTime
90
     *            the new socket release wait time
91
     */
92
    public static void setSocketReleaseWaitTime(int socketReleaseWaitTime) {
93
        _socketReleaseWaitTime = socketReleaseWaitTime;
×
94
    }
×
95

96
    /**
97
     * Returns the number of server sockets that must have been allocated and not returned before waiting for one to be
98
     * returned.
99
     *
100
     * @return the wait threshhold
101
     */
102
    public static int getWaitThreshhold() {
103
        return _waitThreshhold;
×
104
    }
105

106
    /**
107
     * Specifies the number of server sockets that must have been allocated and not returned before waiting for one to
108
     * be returned.
109
     *
110
     * @param waitThreshhold
111
     *            the new wait threshhold
112
     */
113
    public static void setWaitThreshhold(int waitThreshhold) {
114
        _waitThreshhold = waitThreshhold;
×
115
    }
×
116

117
    /**
118
     * Instantiates a new pseudo server.
119
     */
120
    public PseudoServer() {
121
        this(DEFAULT_SOCKET_TIMEOUT);
1✔
122
    }
1✔
123

124
    /**
125
     * create a PseudoServer with the given socketTimeout.
126
     *
127
     * @param socketTimeout
128
     *            - the time out to use
129
     */
130
    public PseudoServer(int socketTimeout) {
1✔
131
        _socketTimeout = socketTimeout;
1✔
132
        _serverNum = ++_numServers;
1✔
133

134
        try {
135
            _serverSocket = new ServerSocket(0);
1✔
136
            _serverSocket.setSoTimeout(1000);
1✔
137
        } catch (IOException e) {
×
138
            System.out.println("Error while creating socket: " + e);
×
139
            throw new RuntimeException(e);
×
140
        }
1✔
141
        Thread t = new Thread("PseudoServer " + _serverNum) {
1✔
142
            @Override
143
            public void run() {
144
                while (_active) {
1✔
145
                    try {
146
                        handleNewConnection(_serverSocket.accept());
1✔
147
                        Thread.sleep(20);
1✔
148
                    } catch (InterruptedIOException e) {
1✔
149
                    } catch (IOException e) {
×
150
                        System.out.println("Error in pseudo server: " + e);
×
151
                        HttpUnitUtils.handleException(e);
×
152
                    } catch (InterruptedException e) {
×
153
                        Thread.interrupted();
×
154
                        System.out.println("Interrupted. Shutting down");
×
155
                        _active = false;
×
156
                    }
1✔
157
                }
158
                try {
159
                    _serverSocket.close();
1✔
160
                } catch (IOException e) {
×
161
                    System.out.println("Error while closing socket: " + e);
×
162
                }
1✔
163
                debug("Pseudoserver shutting down");
1✔
164
            }
1✔
165
        };
166
        debug("Starting pseudoserver");
1✔
167
        t.start();
1✔
168
    }
1✔
169

170
    /**
171
     * Shut down.
172
     */
173
    public void shutDown() {
174
        debug("Requested shutdown of pseudoserver");
1✔
175
        _active = false;
1✔
176
    }
1✔
177

178
    /**
179
     * Debug.
180
     *
181
     * @param message
182
     *            the message
183
     */
184
    private void debug(String message) {
185
        if (!_debug) {
1!
186
            return;
1✔
187
        }
188
        message = replaceDebugToken(message, "thread", "thread (" + Thread.currentThread().getName() + ")");
×
189
        message = replaceDebugToken(message, "server", "server " + _serverNum);
×
190
        System.out.println("** " + message);
×
191
    }
×
192

193
    /**
194
     * Replace debug token.
195
     *
196
     * @param message
197
     *            the message
198
     * @param token
199
     *            the token
200
     * @param replacement
201
     *            the replacement
202
     *
203
     * @return the string
204
     */
205
    private static String replaceDebugToken(String message, String token, String replacement) {
206
        return !message.contains(token) ? message : message.replaceFirst(token, replacement);
×
207
    }
208

209
    /**
210
     * Sets the max protocol level.
211
     *
212
     * @param majorLevel
213
     *            the major level
214
     * @param minorLevel
215
     *            the minor level
216
     */
217
    public void setMaxProtocolLevel(int majorLevel, int minorLevel) {
218
        _maxProtocolLevel = majorLevel + "." + minorLevel;
1✔
219
    }
1✔
220

221
    /**
222
     * Returns the port on which this server is listening.
223
     *
224
     * @return the connected port
225
     *
226
     * @throws IOException
227
     *             Signals that an I/O exception has occurred.
228
     */
229
    public int getConnectedPort() throws IOException {
230
        return _serverSocket.getLocalPort();
1✔
231
    }
232

233
    /**
234
     * Defines the contents of an expected resource.
235
     *
236
     * @param name
237
     *            the name
238
     * @param value
239
     *            the value
240
     */
241
    public void setResource(String name, String value) {
242
        setResource(name, value, "text/html");
1✔
243
    }
1✔
244

245
    /**
246
     * Defines the contents of an expected resource.
247
     *
248
     * @param name
249
     *            the name
250
     * @param servlet
251
     *            the servlet
252
     */
253
    public void setResource(String name, PseudoServlet servlet) {
254
        _resources.put(asResourceName(name), servlet);
1✔
255
    }
1✔
256

257
    /**
258
     * Defines the contents of an expected resource.
259
     *
260
     * @param name
261
     *            the name
262
     * @param value
263
     *            the value
264
     * @param contentType
265
     *            the content type
266
     */
267
    public void setResource(String name, String value, String contentType) {
268
        _resources.put(asResourceName(name), new WebResource(value, contentType));
1✔
269
    }
1✔
270

271
    /**
272
     * Defines the contents of an expected resource.
273
     *
274
     * @param name
275
     *            the name
276
     * @param value
277
     *            the value
278
     * @param contentType
279
     *            the content type
280
     */
281
    public void setResource(String name, byte[] value, String contentType) {
282
        _resources.put(asResourceName(name), new WebResource(value, contentType));
1✔
283
    }
1✔
284

285
    /**
286
     * Defines a resource which will result in an error message. return it for further use
287
     *
288
     * @param name
289
     *            the name
290
     * @param errorCode
291
     *            the error code
292
     * @param errorMessage
293
     *            the error message
294
     *
295
     * @return the resource
296
     */
297
    public WebResource setErrorResource(String name, int errorCode, String errorMessage) {
298
        WebResource resource = new WebResource(errorMessage, errorCode);
1✔
299
        _resources.put(asResourceName(name), resource);
1✔
300
        return resource;
1✔
301
    }
302

303
    /**
304
     * Enables the sending of the character set in the content-type header.
305
     *
306
     * @param name
307
     *            the name
308
     * @param enabled
309
     *            the enabled
310
     */
311
    public void setSendCharacterSet(String name, boolean enabled) {
312
        WebResource resource = (WebResource) _resources.get(asResourceName(name));
1✔
313
        if (resource == null) {
1!
314
            throw new IllegalArgumentException("No defined resource " + name);
×
315
        }
316
        resource.setSendCharacterSet(enabled);
1✔
317
    }
1✔
318

319
    /**
320
     * Specifies the character set encoding for a resource.
321
     *
322
     * @param name
323
     *            the name
324
     * @param characterSet
325
     *            the character set
326
     */
327
    public void setCharacterSet(String name, String characterSet) {
328
        WebResource resource = (WebResource) _resources.get(asResourceName(name));
1✔
329
        if (resource == null) {
1!
330
            resource = new WebResource("");
×
331
            _resources.put(asResourceName(name), resource);
×
332
        }
333
        resource.setCharacterSet(characterSet);
1✔
334
    }
1✔
335

336
    /**
337
     * Adds a header to a defined resource.
338
     *
339
     * @param name
340
     *            the name
341
     * @param header
342
     *            the header
343
     */
344
    public void addResourceHeader(String name, String header) {
345
        WebResource resource = (WebResource) _resources.get(asResourceName(name));
1✔
346
        if (resource == null) {
1!
347
            resource = new WebResource("");
×
348
            _resources.put(asResourceName(name), resource);
×
349
        }
350
        resource.addHeader(header);
1✔
351
    }
1✔
352

353
    /**
354
     * Map to classpath.
355
     *
356
     * @param directory
357
     *            the directory
358
     */
359
    public void mapToClasspath(String directory) {
360
        _classpathDirs.add(directory);
1✔
361
    }
1✔
362

363
    /**
364
     * Sets the debug.
365
     *
366
     * @param debug
367
     *            the new debug
368
     */
369
    public void setDebug(boolean debug) {
370
        _debug = debug;
×
371
    }
×
372

373
    // ------------------------------------- private members ---------------------------------------
374

375
    /** The resources. */
376
    private Hashtable _resources = new Hashtable<>();
1✔
377

378
    /** The active. */
379
    private boolean _active = true;
1✔
380

381
    /** The debug. */
382
    private boolean _debug = false;
1✔
383

384
    /**
385
     * As resource name.
386
     *
387
     * @param rawName
388
     *            the raw name
389
     *
390
     * @return the string
391
     */
392
    private String asResourceName(String rawName) {
393
        if (rawName.startsWith("http:") || rawName.startsWith("/")) {
1✔
394
            return escape(rawName);
1✔
395
        }
396
        return escape("/" + rawName);
1✔
397
    }
398

399
    /**
400
     * Escape.
401
     *
402
     * @param urlString
403
     *            the url string
404
     *
405
     * @return the string
406
     */
407
    private static String escape(String urlString) {
408
        if (urlString.indexOf(' ') < 0) {
1✔
409
            return urlString;
1✔
410
        }
411
        StringBuilder sb = new StringBuilder();
1✔
412

413
        int start = 0;
1✔
414
        do {
415
            int index = urlString.indexOf(' ', start);
1✔
416
            if (index < 0) {
1✔
417
                sb.append(urlString.substring(start));
1✔
418
                break;
1✔
419
            }
420
            sb.append(urlString.substring(start, index)).append("%20");
1✔
421
            start = index + 1;
1✔
422
        } while (true);
1✔
423
        return sb.toString();
1✔
424
    }
425

426
    /**
427
     * Handle new connection.
428
     *
429
     * @param socket
430
     *            the socket
431
     */
432
    private void handleNewConnection(final Socket socket) {
433
        Thread t = new Thread("PseudoServer " + _serverNum + " connection " + (++_connectionNum)) {
1✔
434
            @Override
435
            public void run() {
436
                try {
437
                    serveRequests(socket);
1✔
438
                } catch (IOException e) {
×
439
                    e.printStackTrace(); // To change body of catch statement use Options | File Templates.
×
440
                }
1✔
441
            }
1✔
442
        };
443
        t.start();
1✔
444
    }
1✔
445

446
    /**
447
     * Serve requests.
448
     *
449
     * @param socket
450
     *            the socket
451
     *
452
     * @throws IOException
453
     *             Signals that an I/O exception has occurred.
454
     */
455
    private void serveRequests(Socket socket) throws IOException {
456
        socket.setSoTimeout(_socketTimeout);
1✔
457
        socket.setTcpNoDelay(true);
1✔
458

459
        debug("Created server thread " + socket.getInetAddress() + ':' + socket.getPort());
1✔
460
        final BufferedInputStream inputStream = new BufferedInputStream(socket.getInputStream());
1✔
461
        final HttpResponseStream outputStream = new HttpResponseStream(socket.getOutputStream());
1✔
462

463
        try {
464
            while (_active) {
1✔
465
                HttpRequest request = new HttpRequest(inputStream);
1✔
466
                boolean keepAlive = respondToRequest(request, outputStream);
1✔
467
                if (!keepAlive) {
1✔
468
                    break;
1✔
469
                }
470
                while (_active && 0 == inputStream.available()) {
1✔
471
                    try {
472
                        Thread.sleep(INPUT_POLL_INTERVAL);
1✔
473
                    } catch (InterruptedException e) {
×
474
                        Thread.interrupted();
×
475
                    }
1✔
476
                }
477
            }
1✔
478
        } catch (IOException e) {
1✔
479
            outputStream.restart();
1✔
480
            outputStream.setProtocol("HTTP/1.0");
1✔
481
            outputStream.setResponse(HttpURLConnection.HTTP_BAD_REQUEST, e.toString());
1✔
482
        }
1✔
483
        debug("Closing server thread");
1✔
484
        outputStream.close();
1✔
485
        socket.close();
1✔
486
        debug("Server thread closed");
1✔
487
    }
1✔
488

489
    /**
490
     * respond to the given request.
491
     *
492
     * @param request
493
     *            - the request
494
     * @param response
495
     *            - the response stream
496
     *
497
     * @return true, if successful
498
     */
499
    private boolean respondToRequest(HttpRequest request, HttpResponseStream response) {
500
        debug("Server thread handling request: " + request);
1✔
501
        boolean keepAlive = isKeepAlive(request);
1✔
502
        WebResource resource = null;
1✔
503
        try {
504
            response.restart();
1✔
505
            response.setProtocol(getResponseProtocol(request));
1✔
506
            resource = getResource(request);
1✔
507
            if (resource == null) {
1✔
508
                // what resource could not be find?
509
                String uri = request.getURI();
1✔
510
                // 404 - Not Found error code
511
                int errorCode = HttpURLConnection.HTTP_NOT_FOUND;
1✔
512
                // typical 404 error Message
513
                String errorMessage = "unable to find " + uri;
1✔
514
                // make sure there is a resource and
515
                // next time we'll take it from the resource Cache
516
                resource = setErrorResource(uri, errorCode, errorMessage);
1✔
517
                // set the errorCode for this response
518
                response.setResponse(errorCode, errorMessage);
1✔
519
            } else if (resource.getResponseCode() != HttpURLConnection.HTTP_OK) {
1✔
520
                response.setResponse(resource.getResponseCode(), "");
1✔
521
            }
522
            if (resource.closesConnection()) {
1✔
523
                keepAlive = false;
1✔
524
            }
525
            String[] headers = resource.getHeaders();
1✔
526
            for (String header : headers) {
1✔
527
                debug("Server thread sending header: " + header);
1✔
528
                response.addHeader(header);
1✔
529
            }
530
        } catch (UnknownMethodException e) {
1✔
531
            response.setResponse(HttpURLConnection.HTTP_BAD_METHOD, "unsupported method: " + e.getMethod());
1✔
532
        } catch (Throwable t) {
×
533
            t.printStackTrace();
×
534
            response.setResponse(HttpURLConnection.HTTP_INTERNAL_ERROR, t.toString());
×
535
        }
1✔
536
        try {
537
            response.write(resource);
1✔
538
        } catch (IOException e) {
×
539
            System.out.println("*** Failed to send reply: " + e);
×
540
        }
1✔
541
        return keepAlive;
1✔
542
    }
543

544
    /**
545
     * Checks if is keep alive.
546
     *
547
     * @param request
548
     *            the request
549
     *
550
     * @return true, if is keep alive
551
     */
552
    private boolean isKeepAlive(HttpRequest request) {
553
        return request.wantsKeepAlive() && _maxProtocolLevel.equals("1.1");
1!
554
    }
555

556
    /**
557
     * Gets the response protocol.
558
     *
559
     * @param request
560
     *            the request
561
     *
562
     * @return the response protocol
563
     */
564
    private String getResponseProtocol(HttpRequest request) {
565
        return _maxProtocolLevel.equalsIgnoreCase("1.1") ? request.getProtocol() : "HTTP/1.0";
1✔
566
    }
567

568
    /**
569
     * get the resource for the given request by first trying to look it up in the cache then depending on the type of
570
     * request PseudoServlet and the method / command e.g. GET/HEAD finally the extension of the uri ".zip" ".class" and
571
     * ".jar" are handled
572
     *
573
     * @param request
574
     *            the request
575
     *
576
     * @return the WebResource or null if non of the recipes above will lead to a valid resource
577
     *
578
     * @throws IOException
579
     *             Signals that an I/O exception has occurred.
580
     */
581
    private WebResource getResource(HttpRequest request) throws IOException {
582
        Object resource = _resources.get(request.getURI());
1✔
583
        if (resource == null) {
1✔
584
            resource = _resources.get(withoutParameters(request.getURI()));
1✔
585
        }
586

587
        // check the method of the request
588
        String command = request.getCommand();
1✔
589
        if ((command.equals("GET") || command.equals("HEAD")) && resource instanceof WebResource) {
1✔
590
            return (WebResource) resource;
1✔
591
        }
592
        if (resource instanceof PseudoServlet) {
1✔
593
            return getResource((PseudoServlet) resource, request);
1✔
594
        }
595
        if (request.getURI().endsWith(".class")) {
1✔
596
            for (Iterator iterator = _classpathDirs.iterator(); iterator.hasNext();) {
1!
597
                String directory = (String) iterator.next();
1✔
598
                if (request.getURI().startsWith(directory)) {
1!
599
                    String resourceName = request.getURI().substring(directory.length() + 1);
1✔
600
                    return new WebResource(getClass().getClassLoader().getResourceAsStream(resourceName),
1✔
601
                            "application/class", 200);
602
                }
603
            }
×
604
        } else if (request.getURI().endsWith(".zip") || request.getURI().endsWith(".jar")) {
1!
605
            for (Iterator iterator = _classpathDirs.iterator(); iterator.hasNext();) {
×
606
                String directory = (String) iterator.next();
×
607
                if (request.getURI().startsWith(directory)) {
×
608
                    String resourceName = request.getURI().substring(directory.length() + 1);
×
609
                    String classPath = System.getProperty("java.class.path");
×
610
                    StringTokenizer st = new StringTokenizer(classPath, ":;,");
×
611
                    while (st.hasMoreTokens()) {
×
612
                        String file = st.nextToken();
×
613
                        if (file.endsWith(resourceName)) {
×
614
                            Path f = Path.of(file);
×
615
                            return new WebResource(Files.newInputStream(f), "application/zip", 200);
×
616
                        }
617
                    }
×
618
                }
619
            }
×
620
        }
621
        return null;
1✔
622
    }
623

624
    /**
625
     * Without parameters.
626
     *
627
     * @param uri
628
     *            the uri
629
     *
630
     * @return the string
631
     */
632
    private String withoutParameters(String uri) {
633
        return uri.indexOf('?') < 0 ? uri : uri.substring(0, uri.indexOf('?'));
1✔
634
    }
635

636
    /**
637
     * Gets the resource.
638
     *
639
     * @param servlet
640
     *            the servlet
641
     * @param request
642
     *            the request
643
     *
644
     * @return the resource
645
     *
646
     * @throws IOException
647
     *             Signals that an I/O exception has occurred.
648
     */
649
    private WebResource getResource(PseudoServlet servlet, HttpRequest request) throws IOException {
650
        servlet.init(request);
1✔
651
        return servlet.getResponse(request.getCommand());
1✔
652
    }
653

654
    /** The server socket. */
655
    private ServerSocket _serverSocket;
656

657
}
658

659
class HttpResponseStream {
660

661
    private static final String CRLF = "\r\n";
662

663
    void restart() {
664
        _headersWritten = false;
1✔
665
        _headers.clear();
1✔
666
        _responseCode = HttpURLConnection.HTTP_OK;
1✔
667
        _responseText = "OK";
1✔
668
    }
1✔
669

670
    void close() throws IOException {
671
        flushHeaders();
1✔
672
        _pw.close();
1✔
673
    }
1✔
674

675
    HttpResponseStream(OutputStream stream) {
1✔
676
        _stream = stream;
1✔
677
        setCharacterSet("us-ascii");
1✔
678
    }
1✔
679

680
    void setProtocol(String protocol) {
681
        _protocol = protocol;
1✔
682
    }
1✔
683

684
    /**
685
     * set the response to the given response Code
686
     *
687
     * @param responseCode
688
     * @param responseText
689
     */
690
    void setResponse(int responseCode, String responseText) {
691
        _responseCode = responseCode;
1✔
692
        _responseText = responseText;
1✔
693
    }
1✔
694

695
    void addHeader(String header) {
696
        _headers.add(header);
1✔
697
    }
1✔
698

699
    void write(String contents, String charset) throws IOException {
700
        flushHeaders();
×
701
        setCharacterSet(charset);
×
702
        sendText(contents);
×
703
    }
×
704

705
    void write(WebResource resource) throws IOException {
706
        flushHeaders();
1✔
707
        if (resource != null) {
1✔
708
            resource.writeTo(_stream);
1✔
709
        }
710
        _stream.flush();
1✔
711
    }
1✔
712

713
    private void setCharacterSet(String characterSet) {
714
        if (_pw != null) {
1!
715
            _pw.flush();
×
716
        }
717
        _pw = new PrintWriter(new OutputStreamWriter(_stream, Charset.forName(characterSet)));
1✔
718
    }
1✔
719

720
    private void flushHeaders() {
721
        if (!_headersWritten) {
1✔
722
            sendResponse(_responseCode, _responseText);
1✔
723
            for (Enumeration e = Collections.enumeration(_headers); e.hasMoreElements();) {
1✔
724
                sendLine((String) e.nextElement());
1✔
725
            }
726
            sendText(CRLF);
1✔
727
            _headersWritten = true;
1✔
728
            _pw.flush();
1✔
729
        }
730
    }
1✔
731

732
    private void sendResponse(int responseCode, String responseText) {
733
        sendLine(_protocol + ' ' + responseCode + ' ' + responseText);
1✔
734
    }
1✔
735

736
    private void sendLine(String text) {
737
        sendText(text);
1✔
738
        sendText(CRLF);
1✔
739
    }
1✔
740

741
    private void sendText(String text) {
742
        _pw.write(text);
1✔
743
    }
1✔
744

745
    private OutputStream _stream;
746
    private PrintWriter _pw;
747

748
    private List _headers = new ArrayList<>();
1✔
749
    private String _protocol = "HTTP/1.0";
1✔
750
    private int _responseCode = HttpURLConnection.HTTP_OK;
1✔
751
    private String _responseText = "OK";
1✔
752

753
    private boolean _headersWritten;
754

755
}
756

757
class RecordingOutputStream extends OutputStream {
758

759
    private OutputStream _nestedStream;
760
    private PrintStream _log;
761

762
    public RecordingOutputStream(OutputStream nestedStream, PrintStream log) {
×
763
        _nestedStream = nestedStream;
×
764
        _log = log;
×
765
    }
×
766

767
    @Override
768
    public void write(int b) throws IOException {
769
        _nestedStream.write(b);
×
770
        _log.println("sending " + Integer.toHexString(b));
×
771
    }
×
772

773
    @Override
774
    public void write(byte b[], int offset, int len) throws IOException {
775
        _nestedStream.write(b, offset, len);
×
776
        _log.print("sending");
×
777
        for (int i = offset; i < offset + len; i++) {
×
778
            _log.print(' ' + Integer.toHexString(b[i]));
×
779
        }
780
        _log.println();
×
781
    }
×
782
}
783

784
class RecordingInputStream extends InputStream {
785

786
    private InputStream _nestedStream;
787
    private PrintStream _log;
788

789
    public RecordingInputStream(InputStream nestedStream, PrintStream log) {
×
790
        _nestedStream = nestedStream;
×
791
        _log = log;
×
792
    }
×
793

794
    @Override
795
    public int read() throws IOException {
796
        int value = _nestedStream.read();
×
797
        if (value != -1) {
×
798
            _log.print(' ' + Integer.toHexString(value));
×
799
        }
800
        return value;
×
801
    }
802
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc