blob: bdb9395e8b1ac659786304243f58bf4296474ef1 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.common.io.http;
import java.nio.ByteBuffer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Utf8LineParser;
/**
* Responsible for reading UTF8 Response Header lines and parsing them into a provided UpgradeResponse object.
*/
public class HttpResponseHeaderParser
{
@SuppressWarnings("serial")
public static class ParseException extends RuntimeException
{
public ParseException(String message)
{
super(message);
}
public ParseException(String message, Throwable cause)
{
super(message,cause);
}
}
private enum State
{
STATUS_LINE,
HEADER,
END
}
private static final Pattern PAT_HEADER = Pattern.compile("([^:]+):\\s*(.*)");
private static final Pattern PAT_STATUS_LINE = Pattern.compile("^HTTP/1.[01]\\s+(\\d+)\\s+(.*)",Pattern.CASE_INSENSITIVE);
private final HttpResponseHeaderParseListener listener;
private final Utf8LineParser lineParser;
private State state;
public HttpResponseHeaderParser(HttpResponseHeaderParseListener listener)
{
this.listener = listener;
this.lineParser = new Utf8LineParser();
this.state = State.STATUS_LINE;
}
public boolean isDone()
{
return (state == State.END);
}
public HttpResponseHeaderParseListener parse(ByteBuffer buf) throws ParseException
{
while (!isDone() && (buf.remaining() > 0))
{
String line = lineParser.parse(buf);
if (line != null)
{
if (parseHeader(line))
{
// Now finished with parsing the entire response header
// Save the remaining bytes for WebSocket to process.
ByteBuffer copy = ByteBuffer.allocate(buf.remaining());
BufferUtil.put(buf,copy);
BufferUtil.flipToFlush(copy,0);
this.listener.setRemainingBuffer(copy);
return listener;
}
}
}
return null;
}
private boolean parseHeader(String line) throws ParseException
{
switch (state)
{
case STATUS_LINE:
{
Matcher mat = PAT_STATUS_LINE.matcher(line);
if (!mat.matches())
{
throw new ParseException("Unexpected HTTP response status line [" + line + "]");
}
try
{
listener.setStatusCode(Integer.parseInt(mat.group(1)));
}
catch (NumberFormatException e)
{
throw new ParseException("Unexpected HTTP response status code",e);
}
listener.setStatusReason(mat.group(2));
state = State.HEADER;
break;
}
case HEADER:
{
if (StringUtil.isBlank(line))
{
state = State.END;
return parseHeader(line);
}
Matcher header = PAT_HEADER.matcher(line);
if (header.matches())
{
String headerName = header.group(1);
String headerValue = header.group(2);
// do need to split header/value if comma delimited?
listener.addHeader(headerName,headerValue);
}
break;
}
case END:
state = State.STATUS_LINE;
return true;
}
return false;
}
}