1137
|
1 package luan.webserver;
|
|
2
|
|
3 import luan.lib.parser.Parser;
|
|
4 import luan.lib.parser.ParseException;
|
|
5
|
|
6
|
|
7 final class RequestHeadParser {
|
|
8
|
|
9 static Request parse(String text) throws ParseException {
|
|
10 RequestHeadParser rhp = new RequestHeadParser(text);
|
|
11 rhp.parse();
|
|
12 return rhp.request;
|
|
13 }
|
|
14
|
|
15 private final Request request = new Request();
|
|
16 private final Parser parser;
|
|
17
|
|
18 private RequestHeadParser(String text) {
|
|
19 this.parser = new Parser(text);
|
|
20 request.rawHead = text;
|
|
21 }
|
|
22
|
|
23 private void parse() throws ParseException {
|
|
24 parseRequestLine();
|
|
25 while( !parser.match("\r\n") ) {
|
|
26 parserHeaderField();
|
|
27 }
|
|
28 }
|
|
29
|
|
30
|
|
31 private void parseRequestLine() throws ParseException {
|
|
32 parseMethod();
|
|
33 require( parser.match(' ') );
|
|
34 parsePath();
|
|
35 require( parser.match(' ') );
|
|
36 parseProtocol();
|
|
37 require( parser.match("\r\n") );
|
|
38 }
|
|
39
|
|
40 private void parseMethod() throws ParseException {
|
|
41 int start = parser.currentIndex();
|
|
42 if( !methodChar() )
|
|
43 throw new ParseException(parser,"no method");
|
|
44 while( methodChar() );
|
|
45 request.method = parser.textFrom(start);
|
|
46 }
|
|
47
|
|
48 private boolean methodChar() {
|
|
49 return parser.inCharRange('A','Z');
|
|
50 }
|
|
51
|
|
52 private void parsePath() throws ParseException {
|
|
53 int start = parser.currentIndex();
|
|
54 if( !parser.match('/') )
|
|
55 throw new ParseException(parser,"bad path");
|
|
56 while(
|
|
57 parser.inCharRange('A','Z')
|
|
58 || parser.inCharRange('a','z')
|
|
59 || parser.inCharRange('0','9')
|
|
60 || parser.anyOf("-._~:/?#[]@!$&'()*+,;=`.")
|
|
61 );
|
|
62 request.path = parser.textFrom(start);
|
|
63 }
|
|
64
|
|
65 private void parseProtocol() throws ParseException {
|
|
66 int start = parser.currentIndex();
|
|
67 if( !(
|
|
68 parser.match("HTTP/")
|
|
69 && parser.inCharRange('0','9')
|
|
70 && parser.match('.')
|
|
71 && parser.inCharRange('0','9')
|
|
72 ) )
|
|
73 throw new ParseException(parser,"bad protocol");
|
|
74 request.protocol = parser.textFrom(start);
|
|
75 }
|
|
76
|
|
77
|
|
78 private void parserHeaderField() throws ParseException {
|
|
79 String name = parseName();
|
|
80 require( parser.match(':') );
|
|
81 while( parser.anyOf(" \t") );
|
|
82 String value = parseValue();
|
|
83 while( parser.anyOf(" \t") );
|
|
84 require( parser.match("\r\n") );
|
|
85 request.headers.put(name,value);
|
|
86 }
|
|
87
|
|
88 private String parseName() throws ParseException {
|
|
89 StringBuilder buf = new StringBuilder();
|
|
90 boolean cap = true;
|
|
91 require( tokenChar() );
|
|
92 do {
|
|
93 char c = parser.lastChar();
|
|
94 if( c == '-' ) {
|
|
95 cap = true;
|
|
96 } else if( cap ) {
|
|
97 c = Character.toUpperCase(c);
|
|
98 cap = false;
|
|
99 } else {
|
|
100 c = Character.toLowerCase(c);
|
|
101 }
|
|
102 buf.append(c);
|
|
103 } while( tokenChar() );
|
|
104 return buf.toString();
|
|
105 }
|
|
106
|
|
107 private String parseValue() {
|
|
108 int start = parser.currentIndex();
|
|
109 while( !testEndOfValue() )
|
|
110 parser.anyChar();
|
|
111 return parser.textFrom(start);
|
|
112 }
|
|
113
|
|
114 private boolean testEndOfValue() {
|
|
115 parser.begin();
|
|
116 while( parser.anyOf(" \t") );
|
|
117 boolean b = parser.endOfInput() || parser.anyOf("\r\n");
|
|
118 parser.failure(); // rollback
|
|
119 return b;
|
|
120 }
|
|
121
|
|
122 private void require(boolean b) throws ParseException {
|
|
123 if( !b )
|
|
124 throw new ParseException(parser,"failed");
|
|
125 }
|
|
126
|
|
127 boolean tokenChar() {
|
|
128 if( parser.endOfInput() )
|
|
129 return false;
|
|
130 char c = parser.currentChar();
|
|
131 if( 32 <= c && c <= 126 && "()<>@,;:\\\"/[]?={} \t\r\n".indexOf(c) == -1 ) {
|
|
132 parser.anyChar();
|
|
133 return true;
|
|
134 } else {
|
|
135 return false;
|
|
136 }
|
|
137 }
|
|
138 }
|