1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.websocket.common.io.http;
20
21 import java.nio.ByteBuffer;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24
25 import org.eclipse.jetty.util.BufferUtil;
26 import org.eclipse.jetty.util.StringUtil;
27 import org.eclipse.jetty.util.Utf8LineParser;
28
29
30
31
32 public class HttpResponseHeaderParser
33 {
34 @SuppressWarnings("serial")
35 public static class ParseException extends RuntimeException
36 {
37 public ParseException(String message)
38 {
39 super(message);
40 }
41
42 public ParseException(String message, Throwable cause)
43 {
44 super(message,cause);
45 }
46 }
47
48 private enum State
49 {
50 STATUS_LINE,
51 HEADER,
52 END
53 }
54
55 private static final Pattern PAT_HEADER = Pattern.compile("([^:]+):\\s*(.*)");
56 private static final Pattern PAT_STATUS_LINE = Pattern.compile("^HTTP/1.[01]\\s+(\\d+)\\s+(.*)",Pattern.CASE_INSENSITIVE);
57
58 private final HttpResponseHeaderParseListener listener;
59 private final Utf8LineParser lineParser;
60 private State state;
61
62 public HttpResponseHeaderParser(HttpResponseHeaderParseListener listener)
63 {
64 this.listener = listener;
65 this.lineParser = new Utf8LineParser();
66 this.state = State.STATUS_LINE;
67 }
68
69 public boolean isDone()
70 {
71 return (state == State.END);
72 }
73
74 public HttpResponseHeaderParseListener parse(ByteBuffer buf) throws ParseException
75 {
76 while (!isDone() && (buf.remaining() > 0))
77 {
78 String line = lineParser.parse(buf);
79 if (line != null)
80 {
81 if (parseHeader(line))
82 {
83
84 ByteBuffer copy = ByteBuffer.allocate(buf.remaining());
85 BufferUtil.put(buf,copy);
86 BufferUtil.flipToFlush(copy,0);
87 this.listener.setRemainingBuffer(copy);
88 return listener;
89 }
90 }
91 }
92 return null;
93 }
94
95 private boolean parseHeader(String line) throws ParseException
96 {
97 switch (state)
98 {
99 case STATUS_LINE:
100 {
101 Matcher mat = PAT_STATUS_LINE.matcher(line);
102 if (!mat.matches())
103 {
104 throw new ParseException("Unexpected HTTP response status line [" + line + "]");
105 }
106
107 try
108 {
109 listener.setStatusCode(Integer.parseInt(mat.group(1)));
110 }
111 catch (NumberFormatException e)
112 {
113 throw new ParseException("Unexpected HTTP response status code",e);
114 }
115 listener.setStatusReason(mat.group(2));
116 state = State.HEADER;
117 break;
118 }
119 case HEADER:
120 {
121 if (StringUtil.isBlank(line))
122 {
123 state = State.END;
124 return parseHeader(line);
125 }
126
127 Matcher header = PAT_HEADER.matcher(line);
128 if (header.matches())
129 {
130 String headerName = header.group(1);
131 String headerValue = header.group(2);
132
133 listener.addHeader(headerName,headerValue);
134 }
135 break;
136 }
137 case END:
138 state = State.STATUS_LINE;
139 return true;
140 }
141 return false;
142 }
143 }