1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.coyote;
18
19 import java.io.IOException;
20 import java.util.Iterator;
21 import java.util.Set;
22 import java.util.concurrent.CopyOnWriteArraySet;
23
24 import org.apache.juli.logging.Log;
25 import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
26 import org.apache.tomcat.util.net.DispatchType;
27 import org.apache.tomcat.util.net.SocketEvent;
28 import org.apache.tomcat.util.net.SocketWrapperBase;
29
30 /**
31 * This is a light-weight abstract processor implementation that is intended as
32 * a basis for all Processor implementations from the light-weight upgrade
33 * processors to the HTTP/AJP processors.
34 */
35 public abstract class AbstractProcessorLight implements Processor {
36
37 private Set<DispatchType> dispatches = new CopyOnWriteArraySet<>();
38
39
40 @Override
41 public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
42 throws IOException {
43
44 SocketState state = SocketState.CLOSED;
45 Iterator<DispatchType> dispatches = null;
46 do {
47 if (dispatches != null) {
48 DispatchType nextDispatch = dispatches.next();
49 if (getLog().isDebugEnabled()) {
50 getLog().debug("Processing dispatch type: [" + nextDispatch + "]");
51 }
52 state = dispatch(nextDispatch.getSocketStatus());
53 if (!dispatches.hasNext()) {
54 state = checkForPipelinedData(state, socketWrapper);
55 }
56 } else if (status == SocketEvent.DISCONNECT) {
57 // Do nothing here, just wait for it to get recycled
58 } else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
59 state = dispatch(status);
60 state = checkForPipelinedData(state, socketWrapper);
61 } else if (status == SocketEvent.OPEN_WRITE) {
62 // Extra write event likely after async, ignore
63 state = SocketState.LONG;
64 } else if (status == SocketEvent.OPEN_READ) {
65 state = service(socketWrapper);
66 } else if (status == SocketEvent.CONNECT_FAIL) {
67 logAccess(socketWrapper);
68 } else {
69 // Default to closing the socket if the SocketEvent passed in
70 // is not consistent with the current state of the Processor
71 state = SocketState.CLOSED;
72 }
73
74 if (getLog().isDebugEnabled()) {
75 getLog().debug("Socket: [" + socketWrapper +
76 "], Status in: [" + status +
77 "], State out: [" + state + "]");
78 }
79
80 if (isAsync()) {
81 state = asyncPostProcess();
82 if (getLog().isDebugEnabled()) {
83 getLog().debug("Socket: [" + socketWrapper +
84 "], State after async post processing: [" + state + "]");
85 }
86 }
87
88 if (dispatches == null || !dispatches.hasNext()) {
89 // Only returns non-null iterator if there are
90 // dispatches to process.
91 dispatches = getIteratorAndClearDispatches();
92 }
93 } while (state == SocketState.ASYNC_END ||
94 dispatches != null && state != SocketState.CLOSED);
95
96 return state;
97 }
98
99
100 private SocketState checkForPipelinedData(SocketState inState, SocketWrapperBase<?> socketWrapper)
101 throws IOException {
102 if (inState == SocketState.OPEN) {
103 // There may be pipe-lined data to read. If the data isn't
104 // processed now, execution will exit this loop and call
105 // release() which will recycle the processor (and input
106 // buffer) deleting any pipe-lined data. To avoid this,
107 // process it now.
108 return service(socketWrapper);
109 } else {
110 return inState;
111 }
112 }
113
114
115 public void addDispatch(DispatchType dispatchType) {
116 synchronized (dispatches) {
117 dispatches.add(dispatchType);
118 }
119 }
120
121
122 public Iterator<DispatchType> getIteratorAndClearDispatches() {
123 // Note: Logic in AbstractProtocol depends on this method only returning
124 // a non-null value if the iterator is non-empty. i.e. it should never
125 // return an empty iterator.
126 Iterator<DispatchType> result;
127 synchronized (dispatches) {
128 // Synchronized as the generation of the iterator and the clearing
129 // of dispatches needs to be an atomic operation.
130 result = dispatches.iterator();
131 if (result.hasNext()) {
132 dispatches.clear();
133 } else {
134 result = null;
135 }
136 }
137 return result;
138 }
139
140
141 protected void clearDispatches() {
142 synchronized (dispatches) {
143 dispatches.clear();
144 }
145 }
146
147
148 /**
149 * Add an entry to the access log for a failed connection attempt.
150 *
151 * @param socketWrapper The connection to process
152 *
153 * @throws IOException If an I/O error occurs during the processing of the
154 * request
155 */
156 protected void logAccess(SocketWrapperBase<?> socketWrapper) throws IOException {
157 // NO-OP by default
158 }
159
160
161 /**
162 * Service a 'standard' HTTP request. This method is called for both new
163 * requests and for requests that have partially read the HTTP request line
164 * or HTTP headers. Once the headers have been fully read this method is not
165 * called again until there is a new HTTP request to process. Note that the
166 * request type may change during processing which may result in one or more
167 * calls to {@link #dispatch(SocketEvent)}. Requests may be pipe-lined.
168 *
169 * @param socketWrapper The connection to process
170 *
171 * @return The state the caller should put the socket in when this method
172 * returns
173 *
174 * @throws IOException If an I/O error occurs during the processing of the
175 * request
176 */
177 protected abstract SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException;
178
179 /**
180 * Process an in-progress request that is not longer in standard HTTP mode.
181 * Uses currently include Servlet 3.0 Async and HTTP upgrade connections.
182 * Further uses may be added in the future. These will typically start as
183 * HTTP requests.
184 *
185 * @param status The event to process
186 *
187 * @return The state the caller should put the socket in when this method
188 * returns
189 *
190 * @throws IOException If an I/O error occurs during the processing of the
191 * request
192 */
193 protected abstract SocketState dispatch(SocketEvent status) throws IOException;
194
195 protected abstract SocketState asyncPostProcess();
196
197 protected abstract Log getLog();
198 }
199