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