1 /*
2  * JBoss, Home of Professional Open Source.
3  * Copyright 2014 Red Hat, Inc., and individual contributors
4  * as indicated by the @author tags.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */

18
19 package io.undertow.server.protocol.http;
20
21 import io.undertow.util.HttpString;
22
23 /**
24  * The current state of the tokenizer state machine. This class is mutable and not thread safe.
25  * <p>
26  * As the machine changes state this class is updated rather than allocating a new one each time.
27  *
28  * fields are not private to allow for efficient putfield / getfield access
29  *
30  * Fields can mean different things depending on the current state. This means that names may
31  * not always reflect complete functionality.
32  *
33  * This class is re-used for requests on the same connection.
34  *
35  * @author Stuart Douglas
36  */

37 class ParseState {
38
39     //parsing states
40     public static final int VERB = 0;
41     public static final int PATH = 1;
42     public static final int PATH_PARAMETERS = 2;
43     public static final int QUERY_PARAMETERS = 3;
44     public static final int VERSION = 4;
45     public static final int AFTER_VERSION = 5;
46     public static final int HEADER = 6;
47     public static final int HEADER_VALUE = 7;
48     public static final int PARSE_COMPLETE = 8;
49
50     /**
51      * The actual state of request parsing
52      */

53     int state;
54
55     /**
56      * The current state in the tokenizer state machine.
57      */

58     int parseState;
59
60     /**
61      * If this state is a prefix or terminal match state this is set to the string
62      * that is a candidate to be matched
63      */

64     HttpString current;
65
66     /**
67      * The bytes version of {@link #current}
68      */

69     byte[] currentBytes;
70
71     /**
72      * If this state is a prefix match state then this holds the current position in the string.
73      *
74      */

75     int pos;
76
77     boolean urlDecodeRequired = false;
78
79     /**
80      * If this is in {@link io.undertow.annotationprocessor.AbstractParserGenerator#NO_STATE} then this holds the current token that has been read so far.
81      */

82     final StringBuilder stringBuilder = new StringBuilder();
83
84     /**
85      * We need to keep track of the canonical path
86      */

87     final StringBuilder canonicalPath = new StringBuilder();
88
89     /**
90      * This has different meanings depending on the current state.
91      *
92      * In state {@link #HEADER} it is a the first character of the header, that was read by
93      * {@link #HEADER_VALUE} to see if this was a continuation.
94      *
95      * In state {@link #HEADER_VALUE} if represents the last character that was seen.
96      *
97      */

98     byte leftOver;
99
100
101     /**
102      * This is used to store the next header value when parsing header key / value pairs,
103      */

104     HttpString nextHeader;
105
106     String nextQueryParam;
107
108     int mapCount;
109
110     final StringBuilder decodeBuffer = new StringBuilder();
111
112     /**
113      * In general browsers will often send the same header with every request. This cache allows us to re-use the resulting
114      * strings.
115      */

116     final CacheMap<HttpString, String> headerValuesCache;
117
118     ParseState(int cacheSize) {
119         this.parseState = 0;
120         this.pos = 0;
121         if(cacheSize <= 0) {
122             headerValuesCache = null;
123         } else {
124             headerValuesCache = new CacheMap<>(cacheSize);
125         }
126     }
127
128     public boolean isComplete() {
129         return state == PARSE_COMPLETE;
130     }
131
132     public final void parseComplete(){
133         state = PARSE_COMPLETE;
134     }
135
136     public void reset() {
137         this.state = 0;
138         this.parseState = 0;
139         this.current = null;
140         this.currentBytes = null;
141         this.pos = 0;
142         this.leftOver = 0;
143         this.urlDecodeRequired = false;
144         this.stringBuilder.setLength(0);
145         this.nextHeader = null;
146         this.nextQueryParam = null;
147         this.mapCount = 0;
148     }
149 }
150