/* * Copyright 2013 Simon Thiel * * This file is part of SitJar. * * SitJar is free software: you can redistribute it and/or modify * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SitJar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with SitJar. If not, see <http://www.gnu.org/licenses/lgpl.txt>. */ /* * @author Simon Thiel <simon.thiel at gmx.de> */ package sit.web; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.util.logging.Level; import java.util.logging.Logger; import sit.sstl.ByteBuilder; /** * * @author Simon Thiel <simon.thiel at gmx.de> */ public class HTTPParser { public static final String CONTINUE_100 = "100-continue"; public static final String EXPECT_HEADER_FIELD = "Expect:"; public static final String HTTP_100__CONTINUE = "HTTP/1.1 100 Continue\r\n\r\n"; public int getCRLFCRLFindex(ByteBuilder data) { return data.indexOf(HttpConstants.CRLFCRLF_BYTE); } private boolean proceedToRead(WebBuffer buf, InputStream is, boolean finished) throws IOException { if (finished) { //Logger.getLogger(HTTPParser.class.getName()).log(Level.FINE, "finished"); return false; } if (!buf.isMoreDataToRead()) { // Logger.getLogger(HTTPParser.class.getName()).log(Level.FINE, "no more data"); return false; } if (buf.readFromInputStream(is) > -1) { //Logger.getLogger(HTTPParser.class.getName()).log(Level.FINE, "more data found"); return true; } Logger.getLogger(HTTPParser.class.getName()).log(Level.WARNING, "connection timed out!"); return false; } public HTTPMessage getHeaderAndBody(InputStream is, WebBuffer buf, PrintStream ps) throws IOException, MessageTooLargeException, HTTPParseException { HTTPMessage result = new HTTPMessage(); ByteBuilder data = new ByteBuilder(); buf.init(); //get header while (proceedToRead(buf, is, (result.hasHeader()))) { data.append(buf.getBuffer(), buf.getReadBytes()); int myIndex = getCRLFCRLFindex(data); if (myIndex != -1) { //true in case CRLFCRLF was finaly retrieved and is in data // (this can only be called once since after this result.hasHeader()==finished==true) result.setHeader(new String(data.subSequence(0, myIndex))); //remaining part is reserved for the body if (data.size() > myIndex + HttpConstants.CRLFCRLF_BYTE.length) { data = new ByteBuilder(data.subSequence(myIndex + HttpConstants.CRLFCRLF_BYTE.length)); //remove headerpart from data, but keep additional data read } else { data = new ByteBuilder(); } } checkMaxLenght(data); } //check for missing header caused e.g. by timeout or malformed http call if (!result.hasHeader()) { Logger.getLogger(HTTPParser.class.getName()).log(Level.FINE, "missing header received data (" + data.size() + "):" + data.toString() + "\nread bytes:" + buf.getReadBytes()); return null; } if (result.getWebRequest() == null) { throw new HTTPParseException("WebRequest==null! - data:" + data.toString()); } Logger.getLogger(HTTPParser.class.getName()).log(Level.FINE, "httpCommand:" + result.getWebRequest().httpCommand); if (result.getWebRequest().httpCommand.equalsIgnoreCase(HttpConstants.HTTP_COMMAND_POST) || result.getWebRequest().httpCommand.equalsIgnoreCase(HttpConstants.HTTP_COMMAND_PUT)) { //handle 100 continue issue // 100 (Continue) "HTTP/1.1 100 Continue" if (CONTINUE_100.equalsIgnoreCase(result.getWebRequest().headerItems.get(EXPECT_HEADER_FIELD.toUpperCase()))){ ps.print(HTTP_100__CONTINUE); ps.flush(); Logger.getLogger(HTTPParser.class.getName()).log(Level.INFO, "sent: "+"HTTP/1.1 100 Continue\r\n\r\n"); } //retrieve content length field String contentLengthStr = result.getWebRequest().headerItems.get(HttpConstants.HTTP_HEADER_FIELD_CONTENT_LENGTH.toUpperCase()); long contentLength = Long.MAX_VALUE; if (contentLengthStr != null) { try { contentLength = Long.parseLong(contentLengthStr); } catch (NumberFormatException ex) { //ignore and assume maxlong } } Logger.getLogger(HTTPParser.class.getName()).log(Level.FINE, "contentLength:" + contentLengthStr + "(" + contentLength + ")"); //get body while (proceedToRead(buf, is, (data.size() >= contentLength))) { data.append(buf.getBuffer(), buf.getReadBytes()); checkMaxLenght(data); } result.getWebRequest().body = data.toByteArray(); Logger.getLogger(HTTPParser.class.getName()).log(Level.FINE, "read " + data.size()+ " body data"); Logger.getLogger(HTTPParser.class.getName()).log(Level.FINER, "body data:\n" + result.getWebRequest().body); } return result; } private void checkMaxLenght(ByteBuilder data) throws MessageTooLargeException { if (data.size() > HTTPMessage.MAX_MESSAGE_SIZE) { throw new MessageTooLargeException(); } } }