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

hazendaz / httpunit / 656

06 Dec 2025 09:11PM UTC coverage: 80.452% (+0.02%) from 80.435%
656

push

github

hazendaz
[maven-release-plugin] prepare for next development iteration

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10137 relevant lines covered (81.34%)

0.81 hits per line

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

87.79
/src/main/java/com/meterware/servletunit/InvocationContextImpl.java
1
/*
2
 * MIT License
3
 *
4
 * Copyright 2011-2025 Russell Gold
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
8
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
9
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
12
 * of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
15
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
17
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
 * DEALINGS IN THE SOFTWARE.
19
 */
20
package com.meterware.servletunit;
21

22
import com.meterware.httpunit.AuthorizationRequiredException;
23
import com.meterware.httpunit.FrameSelector;
24
import com.meterware.httpunit.HttpException;
25
import com.meterware.httpunit.WebRequest;
26
import com.meterware.httpunit.WebResponse;
27

28
import java.io.IOException;
29
import java.net.MalformedURLException;
30
import java.net.URL;
31
import java.util.Dictionary;
32
import java.util.Stack;
33

34
import javax.servlet.Filter;
35
import javax.servlet.FilterChain;
36
import javax.servlet.RequestDispatcher;
37
import javax.servlet.Servlet;
38
import javax.servlet.ServletException;
39
import javax.servlet.ServletRequest;
40
import javax.servlet.ServletResponse;
41
import javax.servlet.http.Cookie;
42
import javax.servlet.http.HttpServletRequest;
43
import javax.servlet.http.HttpServletResponse;
44
import javax.servlet.http.HttpSession;
45

46
/**
47
 * This class represents the context in which a specific servlet request is being made. It contains the objects needed
48
 * to unit test the methods of a servlet.
49
 **/
50
class InvocationContextImpl implements InvocationContext {
51

52
    /** The context stack. */
53
    private Stack _contextStack = new Stack();
1✔
54

55
    /** The effective URL. */
56
    private URL _effectiveURL;
57

58
    /** The client. */
59
    private ServletUnitClient _client;
60

61
    /** The application. */
62
    private WebApplication _application;
63

64
    /** The frame. */
65
    private FrameSelector _frame;
66

67
    /** The web response. */
68
    private WebResponse _webResponse;
69

70
    /**
71
     * Returns the request to be processed by the servlet or filter.
72
     **/
73
    @Override
74
    public HttpServletRequest getRequest() {
75
        return getContext().getRequest();
1✔
76
    }
77

78
    /**
79
     * Returns the response which the servlet or filter should modify during its operation.
80
     **/
81
    @Override
82
    public HttpServletResponse getResponse() {
83
        return getContext().getResponse();
1✔
84
    }
85

86
    /**
87
     * Invokes the current servlet or filter.
88
     */
89
    @Override
90
    public void service() throws ServletException, IOException {
91
        if (isFilterActive()) {
1✔
92
            getFilter().doFilter(getRequest(), getResponse(), getFilterChain());
1✔
93
        } else {
94
            getServlet().service(getRequest(), getResponse());
1✔
95
        }
96
    }
1✔
97

98
    /**
99
     * Returns the selected servlet, initialized to provide access to sessions and servlet context information.
100
     **/
101
    @Override
102
    public Servlet getServlet() throws ServletException {
103
        return getContext().getServlet();
1✔
104
    }
105

106
    /**
107
     * Returns the final response from the servlet. Note that this method should only be invoked after all processing
108
     * has been done to the servlet response.
109
     **/
110
    @Override
111
    public WebResponse getServletResponse() throws IOException {
112
        if (_contextStack.size() != 1) {
1!
113
            throw new IllegalStateException("Have not returned from all request dispatchers");
×
114
        }
115
        if (_webResponse == null) {
1!
116
            HttpSession session = getRequest().getSession( /* create */ false);
1✔
117
            if (session != null && session.isNew()) {
1✔
118
                Cookie cookie = new Cookie(ServletUnitHttpSession.SESSION_COOKIE_NAME, session.getId());
1✔
119
                cookie.setPath(_application.getContextPath());
1✔
120
                getResponse().addCookie(cookie);
1✔
121
            }
122
            _webResponse = new ServletUnitWebResponse(_client, _frame, _effectiveURL, getResponse(),
1✔
123
                    _client.getExceptionsThrownOnErrorStatus());
1✔
124
        }
125
        return _webResponse;
1✔
126
    }
127

128
    @Override
129
    public FrameSelector getFrame() {
130
        return _frame;
1✔
131
    }
132

133
    @Override
134
    public void pushIncludeRequest(RequestDispatcher rd, HttpServletRequest request, HttpServletResponse response)
135
            throws ServletException {
136
        if (isFilterActive()) {
1!
137
            throw new IllegalStateException("May not push an include request when no servlet is active");
×
138
        }
139
        _contextStack.push(new ExecutionContext(DispatchedRequestWrapper.createIncludeRequestWrapper(request, rd),
1✔
140
                response, ((RequestDispatcherImpl) rd).getServletMetaData()));
1✔
141
    }
1✔
142

143
    @Override
144
    public void pushForwardRequest(RequestDispatcher rd, HttpServletRequest request, HttpServletResponse response)
145
            throws ServletException {
146
        if (isFilterActive()) {
1!
147
            throw new IllegalStateException("May not push a forward request when no servlet is active");
×
148
        }
149
        _contextStack.push(new ExecutionContext(DispatchedRequestWrapper.createForwardRequestWrapper(request, rd),
1✔
150
                response, ((RequestDispatcherImpl) rd).getServletMetaData()));
1✔
151
    }
1✔
152

153
    @Override
154
    public void popRequest() {
155
        if (getContext().mayPopFilter()) {
1✔
156
            getContext().popFilter();
1✔
157
        } else if (_contextStack.size() == 1) {
1!
158
            throw new IllegalStateException("May not pop the initial request");
×
159
        } else {
160
            _contextStack.pop();
1✔
161
        }
162
    }
1✔
163

164
    @Override
165
    public boolean isFilterActive() {
166
        return getContext().isFilterActive();
1✔
167
    }
168

169
    @Override
170
    public Filter getFilter() throws ServletException {
171
        return getContext().getFilter();
1✔
172
    }
173

174
    @Override
175
    public FilterChain getFilterChain() {
176
        return (servletRequest, servletResponse) -> {
1✔
177
            pushFilter(servletRequest, servletResponse);
1✔
178
            service();
1✔
179
            popRequest();
1✔
180
        };
1✔
181
    }
182

183
    @Override
184
    public void pushFilter(ServletRequest request, ServletResponse response) {
185
        getContext().pushFilter(request, response);
1✔
186
    }
1✔
187

188
    /**
189
     * The Class AccessDeniedException.
190
     */
191
    class AccessDeniedException extends HttpException {
192

193
        /** The Constant serialVersionUID. */
194
        private static final long serialVersionUID = 1L;
195

196
        /**
197
         * Instantiates a new access denied exception.
198
         *
199
         * @param baseURL
200
         *            the base URL
201
         */
202
        public AccessDeniedException(URL baseURL) {
1✔
203
            super(403, "Access Denied", baseURL);
1✔
204
        }
1✔
205
    }
206

207
    // ------------------------------ package methods ---------------------------------------
208

209
    /**
210
     * Constructs a servlet invocation context for a specified servlet container, request, and cookie headers.
211
     *
212
     * @param client
213
     *            the client
214
     * @param runner
215
     *            the runner
216
     * @param frame
217
     *            the frame
218
     * @param request
219
     *            the request
220
     * @param clientHeaders
221
     *            the client headers
222
     * @param messageBody
223
     *            the message body
224
     *
225
     * @throws IOException
226
     *             Signals that an I/O exception has occurred.
227
     * @throws MalformedURLException
228
     *             the malformed URL exception
229
     */
230
    InvocationContextImpl(ServletUnitClient client, ServletRunner runner, FrameSelector frame, WebRequest request,
231
            Dictionary clientHeaders, byte[] messageBody) throws IOException, MalformedURLException {
1✔
232
        _client = client;
1✔
233
        _application = runner.getApplication();
1✔
234
        _frame = frame;
1✔
235

236
        URL requestURL = request.getURL();
1✔
237
        final ServletUnitHttpRequest suhr = new ServletUnitHttpRequest(_application.getServletRequest(requestURL),
1✔
238
                request, runner.getContext(), clientHeaders, messageBody);
1✔
239
        if (_application.usesBasicAuthentication()) {
1✔
240
            suhr.readBasicAuthentication();
1✔
241
        } else if (_application.usesFormAuthentication()) {
1✔
242
            suhr.readFormAuthentication();
1✔
243
        }
244

245
        HttpSession session = suhr.getSession( /* create */ false);
1✔
246
        if (session != null) {
1✔
247
            ((ServletUnitHttpSession) session).access();
1✔
248
        }
249

250
        _effectiveURL = computeEffectiveUrl(suhr, requestURL);
1✔
251
        _contextStack.push(new ExecutionContext(suhr, new ServletUnitHttpResponse(),
1✔
252
                _application.getServletRequest(_effectiveURL)));
1✔
253
    }
1✔
254

255
    /**
256
     * Compute effective url.
257
     *
258
     * @param request
259
     *            the request
260
     * @param requestURL
261
     *            the request URL
262
     *
263
     * @return the url
264
     */
265
    private URL computeEffectiveUrl(HttpServletRequest request, URL requestURL) {
266
        if (!_application.requiresAuthorization(requestURL) || userIsAuthorized(request, requestURL)) {
1✔
267
            return requestURL;
1✔
268
        }
269
        if (request.getRemoteUser() != null) {
1✔
270
            throw new AccessDeniedException(requestURL);
1✔
271
        }
272
        if (_application.usesBasicAuthentication()) {
1✔
273
            throw AuthorizationRequiredException
1✔
274
                    .createBasicAuthenticationRequiredException(_application.getAuthenticationRealm());
1✔
275
        }
276
        if (!_application.usesFormAuthentication()) {
1!
277
            throw new IllegalStateException("Authorization required but no authentication method defined");
×
278
        }
279
        ((ServletUnitHttpSession) request.getSession()).setOriginalURL(requestURL);
1✔
280
        return _application.getLoginURL();
1✔
281
    }
282

283
    /**
284
     * User is authorized.
285
     *
286
     * @param request
287
     *            the request
288
     * @param requestURL
289
     *            the request URL
290
     *
291
     * @return true, if successful
292
     */
293
    private boolean userIsAuthorized(HttpServletRequest request, URL requestURL) {
294
        String[] roles = _application.getPermittedRoles(requestURL);
1✔
295
        for (String role : roles) {
1✔
296
            if (request.isUserInRole(role)) {
1✔
297
                return true;
1✔
298
            }
299
        }
300
        return false;
1✔
301
    }
302

303
    /**
304
     * The Class ExecutionContext.
305
     */
306
    static class ExecutionContext {
307

308
        /** The response. */
309
        private HttpServletResponse _response;
310

311
        /** The request. */
312
        private HttpServletRequest _request;
313

314
        /** The meta data. */
315
        private ServletMetaData _metaData;
316

317
        /** The filter stack. */
318
        private Stack _filterStack = new Stack();
1✔
319

320
        /**
321
         * Instantiates a new execution context.
322
         *
323
         * @param request
324
         *            the request
325
         * @param response
326
         *            the response
327
         * @param metaData
328
         *            the meta data
329
         */
330
        ExecutionContext(HttpServletRequest request, HttpServletResponse response, ServletMetaData metaData) {
1✔
331
            _request = request;
1✔
332
            _response = response;
1✔
333
            _metaData = metaData;
1✔
334
        }
1✔
335

336
        /**
337
         * Checks if is filter active.
338
         *
339
         * @return true, if is filter active
340
         */
341
        boolean isFilterActive() {
342
            return getFilterIndex() < _metaData.getFilters().length;
1✔
343
        }
344

345
        /**
346
         * Gets the servlet.
347
         *
348
         * @return the servlet
349
         *
350
         * @throws ServletException
351
         *             the servlet exception
352
         */
353
        Servlet getServlet() throws ServletException {
354
            if (isFilterActive()) {
1!
355
                throw new IllegalStateException("Current context is a filter - may not request servlet.");
×
356
            }
357
            return _metaData.getServlet();
1✔
358
        }
359

360
        /**
361
         * Gets the response.
362
         *
363
         * @return the response
364
         */
365
        HttpServletResponse getResponse() {
366
            return _filterStack.isEmpty() ? _response : ((FilterContext) _filterStack.lastElement()).getResponse();
1✔
367
        }
368

369
        /**
370
         * Gets the request.
371
         *
372
         * @return the request
373
         */
374
        HttpServletRequest getRequest() {
375
            return _filterStack.isEmpty() ? _request : ((FilterContext) _filterStack.lastElement()).getRequest();
1✔
376
        }
377

378
        /**
379
         * Gets the filter.
380
         *
381
         * @return the filter
382
         *
383
         * @throws ServletException
384
         *             the servlet exception
385
         */
386
        public Filter getFilter() throws ServletException {
387
            if (!isFilterActive()) {
1!
388
                throw new IllegalStateException("Current context is a servlet - may not request filter.");
×
389
            }
390
            return _metaData.getFilters()[getFilterIndex()].getFilter();
1✔
391
        }
392

393
        /**
394
         * Push filter.
395
         *
396
         * @param request
397
         *            the request
398
         * @param response
399
         *            the response
400
         */
401
        public void pushFilter(ServletRequest request, ServletResponse response) {
402
            if (!isFilterActive()) {
1!
403
                throw new IllegalStateException("Current context is a servlet - may not push filter.");
×
404
            }
405
            if (!(request instanceof HttpServletRequest)) {
1!
406
                throw new IllegalArgumentException("HttpUnit does not support non-HTTP request wrappers");
×
407
            }
408
            if (!(response instanceof HttpServletResponse)) {
1!
409
                throw new IllegalArgumentException("HttpUnit does not support non-HTTP response wrappers");
×
410
            }
411

412
            _filterStack.push(new FilterContext((HttpServletRequest) request, (HttpServletResponse) response));
1✔
413
        }
1✔
414

415
        /**
416
         * May pop filter.
417
         *
418
         * @return true, if successful
419
         */
420
        public boolean mayPopFilter() {
421
            return getFilterIndex() > 0;
1✔
422
        }
423

424
        /**
425
         * Pop filter.
426
         */
427
        public void popFilter() {
428
            _filterStack.pop();
1✔
429
        }
1✔
430

431
        /**
432
         * Gets the filter index.
433
         *
434
         * @return the filter index
435
         */
436
        private int getFilterIndex() {
437
            return _filterStack.size();
1✔
438
        }
439
    }
440

441
    /**
442
     * The Class FilterContext.
443
     */
444
    static class FilterContext {
445

446
        /** The request. */
447
        HttpServletRequest _request;
448

449
        /** The response. */
450
        HttpServletResponse _response;
451

452
        /**
453
         * Instantiates a new filter context.
454
         *
455
         * @param request
456
         *            the request
457
         * @param response
458
         *            the response
459
         */
460
        public FilterContext(HttpServletRequest request, HttpServletResponse response) {
1✔
461
            _request = request;
1✔
462
            _response = response;
1✔
463
        }
1✔
464

465
        /**
466
         * Gets the response.
467
         *
468
         * @return the response
469
         */
470
        public HttpServletResponse getResponse() {
471
            return _response;
1✔
472
        }
473

474
        /**
475
         * Gets the request.
476
         *
477
         * @return the request
478
         */
479
        public HttpServletRequest getRequest() {
480
            return _request;
1✔
481
        }
482
    }
483

484
    /**
485
     * Gets the context.
486
     *
487
     * @return the context
488
     */
489
    private ExecutionContext getContext() {
490
        return (ExecutionContext) _contextStack.lastElement();
1✔
491
    }
492
}
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