View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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  
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.StringUtil;
26  import org.eclipse.jetty.util.Utf8LineParser;
27  import org.eclipse.jetty.websocket.api.UpgradeException;
28  import org.eclipse.jetty.websocket.client.ClientUpgradeResponse;
29  
30  /**
31   * Responsible for reading UTF8 Response Header lines and parsing them into a provided UpgradeResponse object.
32   */
33  public class HttpResponseHeaderParser
34  {
35      private enum State
36      {
37          STATUS_LINE,
38          HEADER,
39          END
40      }
41  
42      private static final Pattern PAT_HEADER = Pattern.compile("([^:]+):\\s*(.*)");
43      private static final Pattern PAT_STATUS_LINE = Pattern.compile("^HTTP/1.[01]\\s+(\\d+)\\s+(.*)",Pattern.CASE_INSENSITIVE);
44  
45      private ClientUpgradeResponse response;
46      private Utf8LineParser lineParser;
47      private State state;
48  
49      public HttpResponseHeaderParser()
50      {
51          this.lineParser = new Utf8LineParser();
52          this.state = State.STATUS_LINE;
53      }
54  
55      public boolean isDone()
56      {
57          return (state == State.END);
58      }
59  
60      public ClientUpgradeResponse parse(ByteBuffer buf) throws UpgradeException
61      {
62          while (!isDone() && (buf.remaining() > 0))
63          {
64              String line = lineParser.parse(buf);
65              if (line != null)
66              {
67                  if (parseHeader(line))
68                  {
69                      return this.response;
70                  }
71              }
72          }
73          return null;
74      }
75  
76      private boolean parseHeader(String line)
77      {
78          switch (state)
79          {
80              case STATUS_LINE:
81              {
82                  this.response = new ClientUpgradeResponse();
83                  Matcher mat = PAT_STATUS_LINE.matcher(line);
84                  if (!mat.matches())
85                  {
86                      throw new UpgradeException("Unexpected HTTP upgrade response status line [" + line + "]");
87                  }
88  
89                  try
90                  {
91                      response.setStatusCode(Integer.parseInt(mat.group(1)));
92                  }
93                  catch (NumberFormatException e)
94                  {
95                      throw new UpgradeException("Unexpected HTTP upgrade response status code",e);
96                  }
97                  response.setStatusReason(mat.group(2));
98                  state = State.HEADER;
99                  break;
100             }
101             case HEADER:
102             {
103                 if (StringUtil.isBlank(line))
104                 {
105                     state = State.END;
106                     return parseHeader(line);
107                 }
108 
109                 Matcher header = PAT_HEADER.matcher(line);
110                 if (header.matches())
111                 {
112                     String headerName = header.group(1);
113                     String headerValue = header.group(2);
114                     // TODO: need to split header/value if comma delimited
115                     response.addHeader(headerName,headerValue);
116                 }
117                 break;
118             }
119             case END:
120                 state = State.STATUS_LINE;
121                 return true;
122         }
123         return false;
124     }
125 }