1/* 2 * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * - Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * - Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * - Neither the name of Oracle nor the names of its 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/* 33 * This source code is provided to illustrate the usage of a given feature 34 * or technique and has been deliberately simplified. Additional steps 35 * required for a production-quality application, such as security checks, 36 * input validation and proper error handling, might not be present in 37 * this sample code. 38 */ 39 40 41import java.net.*; 42import java.nio.*; 43import java.nio.charset.*; 44import java.util.regex.*; 45 46/** 47 * An encapsulation of the request received. 48 * <P> 49 * The static method parse() is responsible for creating this 50 * object. 51 * 52 * @author Mark Reinhold 53 * @author Brad R. Wetmore 54 */ 55class Request { 56 57 /** 58 * A helper class for parsing HTTP command actions. 59 */ 60 static class Action { 61 62 private String name; 63 private Action(String name) { this.name = name; } 64 public String toString() { return name; } 65 66 static Action GET = new Action("GET"); 67 static Action PUT = new Action("PUT"); 68 static Action POST = new Action("POST"); 69 static Action HEAD = new Action("HEAD"); 70 71 static Action parse(String s) { 72 if (s.equals("GET")) 73 return GET; 74 if (s.equals("PUT")) 75 return PUT; 76 if (s.equals("POST")) 77 return POST; 78 if (s.equals("HEAD")) 79 return HEAD; 80 throw new IllegalArgumentException(s); 81 } 82 } 83 84 private Action action; 85 private String version; 86 private URI uri; 87 88 Action action() { return action; } 89 String version() { return version; } 90 URI uri() { return uri; } 91 92 private Request(Action a, String v, URI u) { 93 action = a; 94 version = v; 95 uri = u; 96 } 97 98 public String toString() { 99 return (action + " " + version + " " + uri); 100 } 101 102 static boolean isComplete(ByteBuffer bb) { 103 int p = bb.position() - 4; 104 if (p < 0) 105 return false; 106 return (((bb.get(p + 0) == '\r') && 107 (bb.get(p + 1) == '\n') && 108 (bb.get(p + 2) == '\r') && 109 (bb.get(p + 3) == '\n'))); 110 } 111 112 private static Charset ascii = Charset.forName("US-ASCII"); 113 114 /* 115 * The expected message format is first compiled into a pattern, 116 * and is then compared against the inbound character buffer to 117 * determine if there is a match. This convienently tokenizes 118 * our request into usable pieces. 119 * 120 * This uses Matcher "expression capture groups" to tokenize 121 * requests like: 122 * 123 * GET /dir/file HTTP/1.1 124 * Host: hostname 125 * 126 * into: 127 * 128 * group[1] = "GET" 129 * group[2] = "/dir/file" 130 * group[3] = "1.1" 131 * group[4] = "hostname" 132 * 133 * The text in between the parens are used to captured the regexp text. 134 */ 135 private static Pattern requestPattern 136 = Pattern.compile("\\A([A-Z]+) +([^ ]+) +HTTP/([0-9\\.]+)$" 137 + ".*^Host: ([^ ]+)$.*\r\n\r\n\\z", 138 Pattern.MULTILINE | Pattern.DOTALL); 139 140 static Request parse(ByteBuffer bb) throws MalformedRequestException { 141 142 CharBuffer cb = ascii.decode(bb); 143 Matcher m = requestPattern.matcher(cb); 144 if (!m.matches()) 145 throw new MalformedRequestException(); 146 Action a; 147 try { 148 a = Action.parse(m.group(1)); 149 } catch (IllegalArgumentException x) { 150 throw new MalformedRequestException(); 151 } 152 URI u; 153 try { 154 u = new URI("http://" 155 + m.group(4) 156 + m.group(2)); 157 } catch (URISyntaxException x) { 158 throw new MalformedRequestException(); 159 } 160 return new Request(a, m.group(3), u); 161 } 162} 163