1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19package org.eclipse.jetty.util;
20
21import java.io.BufferedInputStream;
22import java.io.IOException;
23import java.io.InputStream;
24
25/**
26 * ReadLineInputStream
27 *
28 * Read from an input stream, accepting CR/LF, LF or just CR.
29 */
30public class ReadLineInputStream extends BufferedInputStream
31{
32    boolean _seenCRLF;
33    boolean _skipLF;
34
35    public ReadLineInputStream(InputStream in)
36    {
37        super(in);
38    }
39
40    public ReadLineInputStream(InputStream in, int size)
41    {
42        super(in,size);
43    }
44
45    public String readLine() throws IOException
46    {
47        mark(buf.length);
48
49        while (true)
50        {
51            int b=super.read();
52
53            if (markpos < 0)
54                throw new IOException("Buffer size exceeded: no line terminator");
55
56            if (b==-1)
57            {
58                int m=markpos;
59                markpos=-1;
60                if (pos>m)
61                    return new String(buf,m,pos-m,StringUtil.__UTF8_CHARSET);
62
63                return null;
64            }
65
66            if (b=='\r')
67            {
68                int p=pos;
69
70                // if we have seen CRLF before, hungrily consume LF
71                if (_seenCRLF && pos<count)
72                {
73                    if (buf[pos]=='\n')
74                        pos+=1;
75                }
76                else
77                    _skipLF=true;
78                int m=markpos;
79                markpos=-1;
80                return new String(buf,m,p-m-1,StringUtil.__UTF8_CHARSET);
81            }
82
83            if (b=='\n')
84            {
85                if (_skipLF)
86                {
87                    _skipLF=false;
88                    _seenCRLF=true;
89                    markpos++;
90                    continue;
91                }
92                int m=markpos;
93                markpos=-1;
94                return new String(buf,m,pos-m-1,StringUtil.__UTF8_CHARSET);
95            }
96        }
97    }
98
99    @Override
100    public synchronized int read() throws IOException
101    {
102        int b = super.read();
103        if (_skipLF)
104        {
105            _skipLF=false;
106            if (_seenCRLF && b=='\n')
107                b=super.read();
108        }
109        return b;
110    }
111
112    @Override
113    public synchronized int read(byte[] buf, int off, int len) throws IOException
114    {
115        if (_skipLF && len>0)
116        {
117            _skipLF=false;
118            if (_seenCRLF)
119            {
120                int b = super.read();
121                if (b==-1)
122                    return -1;
123
124                if (b!='\n')
125                {
126                    buf[off]=(byte)(0xff&b);
127                    return 1+super.read(buf,off+1,len-1);
128                }
129            }
130        }
131
132        return super.read(buf,off,len);
133    }
134
135
136}
137