1
18
19 package io.undertow.server.protocol;
20
21 import io.undertow.UndertowLogger;
22 import io.undertow.server.ServerConnection;
23 import io.undertow.util.WorkerUtils;
24 import org.xnio.IoUtils;
25 import org.xnio.XnioExecutor;
26 import org.xnio.channels.ConnectedChannel;
27
28 import java.io.Closeable;
29 import java.util.concurrent.RejectedExecutionException;
30 import java.util.concurrent.TimeUnit;
31
32
38 public final class ParseTimeoutUpdater implements Runnable, ServerConnection.CloseListener, Closeable {
39
40 private final ConnectedChannel connection;
41 private final long requestParseTimeout;
42 private final long requestIdleTimeout;
43 private volatile XnioExecutor.Key handle;
44 private volatile long expireTime = -1;
45 private volatile boolean parsing = false;
46
47
48 private static final int FUZZ_FACTOR = 50;
49
50 private final Runnable closeTask;
51
52
53
59 public ParseTimeoutUpdater(ConnectedChannel channel, long requestParseTimeout, long requestIdleTimeout) {
60 this(channel, requestParseTimeout, requestIdleTimeout, new Runnable() {
61 @Override
62 public void run() {
63 IoUtils.safeClose(channel);
64 }
65 });
66 }
67
68
74 public ParseTimeoutUpdater(ConnectedChannel channel, long requestParseTimeout, long requestIdleTimeout, Runnable closeTask) {
75 this.connection = channel;
76 this.requestParseTimeout = requestParseTimeout;
77 this.requestIdleTimeout = requestIdleTimeout;
78 this.closeTask = closeTask;
79 }
80
83 public void connectionIdle() {
84 parsing = false;
85 handleSchedule(requestIdleTimeout);
86 }
87
88 private void handleSchedule(long timeout) {
89
90 if(timeout == -1) {
91 this.expireTime = -1;
92 return;
93 }
94
95 long newExpireTime = System.currentTimeMillis() + timeout;
96 long oldExpireTime = this.expireTime;
97 this.expireTime = newExpireTime;
98
99 if(newExpireTime < oldExpireTime) {
100 if(handle != null) {
101 handle.remove();
102 handle = null;
103 }
104 }
105 if(handle == null) {
106 try {
107 handle = WorkerUtils.executeAfter(connection.getIoThread(), this, timeout + FUZZ_FACTOR, TimeUnit.MILLISECONDS);
108 } catch (RejectedExecutionException e) {
109 UndertowLogger.REQUEST_LOGGER.debug("Failed to schedule parse timeout, server is probably shutting down", e);
110 }
111 }
112 }
113
114
119 public void failedParse() {
120 if(!parsing) {
121 parsing = true;
122 handleSchedule(requestParseTimeout);
123 }
124 }
125
126
132 public void requestStarted() {
133 expireTime = -1;
134 parsing = false;
135 }
136
137 @Override
138 public void run() {
139 if(!connection.isOpen()) {
140 return;
141 }
142 handle = null;
143 if (expireTime > 0) {
144 long now = System.currentTimeMillis();
145 if(expireTime > now) {
146 handle = WorkerUtils.executeAfter(connection.getIoThread(), this, (expireTime - now) + FUZZ_FACTOR, TimeUnit.MILLISECONDS);
147 } else {
148 if(parsing) {
149 UndertowLogger.REQUEST_LOGGER.parseRequestTimedOut(connection.getPeerAddress());
150 } else {
151 UndertowLogger.REQUEST_LOGGER.debugf("Timing out idle connection from %s", connection.getPeerAddress());
152 }
153 closeTask.run();
154 }
155 }
156 }
157
158 @Override
159 public void closed(ServerConnection connection) {
160 close();
161 }
162
163 public void close() {
164 if(handle != null) {
165 handle.remove();
166 handle = null;
167 }
168 }
169 }
170