001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.test.web;
017
018import javax.servlet.ReadListener;
019import javax.servlet.ServletInputStream;
020import java.io.IOException;
021import java.nio.charset.StandardCharsets;
022
023/**
024 * 参考:https://stackoverflow.com/questions/30484388/inputstream-to-servletinputstream
025 */
026public class MockServletInputStream extends ServletInputStream {
027
028    final byte[] myBytes;
029
030    private int lastIndexRetrieved = -1;
031    private ReadListener readListener = null;
032    private int readLimit = -1;
033    private int markedPosition = -1;
034
035    public MockServletInputStream(byte[] myBytes) {
036        this.myBytes = myBytes;
037    }
038
039    public MockServletInputStream(String content) {
040        myBytes = content.getBytes(StandardCharsets.UTF_8);
041    }
042
043    @Override
044    public boolean isFinished() {
045        return (lastIndexRetrieved == myBytes.length - 1);
046    }
047
048    @Override
049    public boolean isReady() {
050        return isFinished();
051    }
052
053    @Override
054    public int available() throws IOException {
055        return (myBytes.length - lastIndexRetrieved - 1);
056    }
057
058    @Override
059    public void close() throws IOException {
060        lastIndexRetrieved = myBytes.length - 1;
061    }
062
063    @Override
064    public void setReadListener(ReadListener readListener) {
065        this.readListener = readListener;
066        if (!isFinished()) {
067            try {
068                readListener.onDataAvailable();
069            } catch (IOException e) {
070                readListener.onError(e);
071            }
072        } else {
073            try {
074                readListener.onAllDataRead();
075            } catch (IOException e) {
076                readListener.onError(e);
077            }
078        }
079    }
080
081
082    @Override
083    public boolean markSupported() {
084        return true;
085    }
086
087    @Override
088    public synchronized void mark(int readLimit) {
089        this.readLimit = readLimit;
090        this.markedPosition = lastIndexRetrieved;
091    }
092
093    @Override
094    public synchronized void reset() throws IOException {
095        if (markedPosition == -1) {
096            throw new IOException("No mark found");
097        } else {
098            lastIndexRetrieved = markedPosition;
099            readLimit = -1;
100        }
101    }
102
103    // Replacement of earlier read method to cope with readLimit
104    @Override
105    public int read() throws IOException {
106        int i;
107        if (!isFinished()) {
108            i = myBytes[lastIndexRetrieved + 1];
109            lastIndexRetrieved++;
110            if (isFinished() && (readListener != null)) {
111                try {
112                    readListener.onAllDataRead();
113                } catch (IOException ex) {
114                    readListener.onError(ex);
115                    throw ex;
116                }
117                readLimit = -1;
118            }
119            if (readLimit != -1) {
120                if ((lastIndexRetrieved - markedPosition) > readLimit) {
121                    // This part is actually not necessary in our implementation
122                    // as we are not storing any data. However we need to respect
123                    // the contract.
124                    markedPosition = -1;
125                    readLimit = -1;
126                }
127            }
128            return i;
129        } else {
130            return -1;
131        }
132    }
133}