1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.websocket.client.io;
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 import org.eclipse.jetty.websocket.client.ClientUpgradeResponse;
29
30
31
32
33 public class HttpResponseHeaderParser
34 {
35 @SuppressWarnings("serial")
36 public static class ParseException extends RuntimeException
37 {
38 public ParseException(String message)
39 {
40 super(message);
41 }
42
43 public ParseException(String message, Throwable cause)
44 {
45 super(message,cause);
46 }
47 }
48
49 private enum State
50 {
51 STATUS_LINE,
52 HEADER,
53 END
54 }
55
56 private static final Pattern PAT_HEADER = Pattern.compile("([^:]+):\\s*(.*)");
57 private static final Pattern PAT_STATUS_LINE = Pattern.compile("^HTTP/1.[01]\\s+(\\d+)\\s+(.*)",Pattern.CASE_INSENSITIVE);
58
59 private ClientUpgradeResponse response;
60 private Utf8LineParser lineParser;
61 private State state;
62
63 public HttpResponseHeaderParser()
64 {
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 ClientUpgradeResponse 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.response.setRemainingBuffer(copy);
88 return this.response;
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 this.response = new ClientUpgradeResponse();
102 Matcher mat = PAT_STATUS_LINE.matcher(line);
103 if (!mat.matches())
104 {
105 throw new ParseException("Unexpected HTTP upgrade response status line [" + line + "]");
106 }
107
108 try
109 {
110 response.setStatusCode(Integer.parseInt(mat.group(1)));
111 }
112 catch (NumberFormatException e)
113 {
114 throw new ParseException("Unexpected HTTP upgrade response status code",e);
115 }
116 response.setStatusReason(mat.group(2));
117 state = State.HEADER;
118 break;
119 }
120 case HEADER:
121 {
122 if (StringUtil.isBlank(line))
123 {
124 state = State.END;
125 return parseHeader(line);
126 }
127
128 Matcher header = PAT_HEADER.matcher(line);
129 if (header.matches())
130 {
131 String headerName = header.group(1);
132 String headerValue = header.group(2);
133
134 response.addHeader(headerName,headerValue);
135 }
136 break;
137 }
138 case END:
139 state = State.STATUS_LINE;
140 return true;
141 }
142 return false;
143 }
144 }