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
85
86 ByteBuffer copy = ByteBuffer.allocate(buf.remaining());
87 BufferUtil.put(buf,copy);
88 BufferUtil.flipToFlush(copy,0);
89 this.listener.setRemainingBuffer(copy);
90 return listener;
91 }
92 }
93 }
94 return null;
95 }
96
97 private boolean parseHeader(String line) throws ParseException
98 {
99 switch (state)
100 {
101 case STATUS_LINE:
102 {
103 Matcher mat = PAT_STATUS_LINE.matcher(line);
104 if (!mat.matches())
105 {
106 throw new ParseException("Unexpected HTTP response status line [" + line + "]");
107 }
108
109 try
110 {
111 listener.setStatusCode(Integer.parseInt(mat.group(1)));
112 }
113 catch (NumberFormatException e)
114 {
115 throw new ParseException("Unexpected HTTP response status code",e);
116 }
117 listener.setStatusReason(mat.group(2));
118 state = State.HEADER;
119 break;
120 }
121 case HEADER:
122 {
123 if (StringUtil.isBlank(line))
124 {
125 state = State.END;
126 return parseHeader(line);
127 }
128
129 Matcher header = PAT_HEADER.matcher(line);
130 if (header.matches())
131 {
132 String headerName = header.group(1);
133 String headerValue = header.group(2);
134
135 listener.addHeader(headerName,headerValue);
136 }
137 break;
138 }
139 case END:
140 state = State.STATUS_LINE;
141 return true;
142 }
143 return false;
144 }
145 }