View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.fcgi.server;
20  
21  import java.nio.ByteBuffer;
22  import java.util.concurrent.ConcurrentHashMap;
23  import java.util.concurrent.ConcurrentMap;
24  
25  import org.eclipse.jetty.fcgi.FCGI;
26  import org.eclipse.jetty.fcgi.generator.Flusher;
27  import org.eclipse.jetty.fcgi.parser.ServerParser;
28  import org.eclipse.jetty.http.HttpField;
29  import org.eclipse.jetty.io.AbstractConnection;
30  import org.eclipse.jetty.io.ByteBufferPool;
31  import org.eclipse.jetty.io.EndPoint;
32  import org.eclipse.jetty.server.ByteBufferQueuedHttpInput;
33  import org.eclipse.jetty.server.Connector;
34  import org.eclipse.jetty.server.HttpConfiguration;
35  import org.eclipse.jetty.util.log.Log;
36  import org.eclipse.jetty.util.log.Logger;
37  
38  public class ServerFCGIConnection extends AbstractConnection
39  {
40      private static final Logger LOG = Log.getLogger(ServerFCGIConnection.class);
41  
42      private final ConcurrentMap<Integer, HttpChannelOverFCGI> channels = new ConcurrentHashMap<>();
43      private final Connector connector;
44      private final boolean sendStatus200;
45      private final Flusher flusher;
46      private final HttpConfiguration configuration;
47      private final ServerParser parser;
48  
49      public ServerFCGIConnection(Connector connector, EndPoint endPoint, HttpConfiguration configuration, boolean sendStatus200)
50      {
51          super(endPoint, connector.getExecutor());
52          this.connector = connector;
53          this.flusher = new Flusher(endPoint);
54          this.configuration = configuration;
55          this.sendStatus200 = sendStatus200;
56          this.parser = new ServerParser(new ServerListener());
57      }
58  
59      @Override
60      public void onOpen()
61      {
62          super.onOpen();
63          fillInterested();
64      }
65  
66      @Override
67      public void onFillable()
68      {
69          EndPoint endPoint = getEndPoint();
70          ByteBufferPool bufferPool = connector.getByteBufferPool();
71          ByteBuffer buffer = bufferPool.acquire(configuration.getResponseHeaderSize(), true);
72          try
73          {
74              while (true)
75              {
76                  int read = endPoint.fill(buffer);
77                  if (LOG.isDebugEnabled()) // Avoid boxing of variable 'read'
78                      LOG.debug("Read {} bytes from {}", read, endPoint);
79                  if (read > 0)
80                  {
81                      parse(buffer);
82                  }
83                  else if (read == 0)
84                  {
85                      fillInterested();
86                      break;
87                  }
88                  else
89                  {
90                      shutdown();
91                      break;
92                  }
93              }
94          }
95          catch (Exception x)
96          {
97              LOG.debug(x);
98              // TODO: fail and close ?
99          }
100         finally
101         {
102             bufferPool.release(buffer);
103         }
104     }
105 
106     private void parse(ByteBuffer buffer)
107     {
108         while (buffer.hasRemaining())
109             parser.parse(buffer);
110     }
111 
112     private void shutdown()
113     {
114         flusher.shutdown();
115     }
116 
117     private class ServerListener implements ServerParser.Listener
118     {
119         @Override
120         public void onStart(int request, FCGI.Role role, int flags)
121         {
122             // TODO: handle flags
123             HttpChannelOverFCGI channel = new HttpChannelOverFCGI(connector, configuration, getEndPoint(),
124                     new HttpTransportOverFCGI(connector.getByteBufferPool(), flusher, request, sendStatus200),
125                     new ByteBufferQueuedHttpInput());
126             HttpChannelOverFCGI existing = channels.putIfAbsent(request, channel);
127             if (existing != null)
128                 throw new IllegalStateException();
129             if (LOG.isDebugEnabled())
130                 LOG.debug("Request {} start on {}", request, channel);
131         }
132 
133         @Override
134         public void onHeader(int request, HttpField field)
135         {
136             HttpChannelOverFCGI channel = channels.get(request);
137             if (LOG.isDebugEnabled())
138                 LOG.debug("Request {} header {} on {}", request, field, channel);
139             if (channel != null)
140                 channel.header(field);
141         }
142 
143         @Override
144         public void onHeaders(int request)
145         {
146             HttpChannelOverFCGI channel = channels.get(request);
147             if (LOG.isDebugEnabled())
148                 LOG.debug("Request {} headers on {}", request, channel);
149             if (channel != null)
150             {
151                 if (channel.headerComplete())
152                     channel.dispatch();
153             }
154         }
155 
156         @Override
157         public boolean onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
158         {
159             HttpChannelOverFCGI channel = channels.get(request);
160             if (LOG.isDebugEnabled())
161                 LOG.debug("Request {} {} content {} on {}", request, stream, buffer, channel);
162             if (channel != null)
163             {
164                 if (channel.content(buffer))
165                     channel.dispatch();
166             }
167             return false;
168         }
169 
170         @Override
171         public void onEnd(int request)
172         {
173             HttpChannelOverFCGI channel = channels.remove(request);
174             if (LOG.isDebugEnabled())
175                 LOG.debug("Request {} end on {}", request, channel);
176             if (channel != null)
177             {
178                 if (channel.messageComplete())
179                     channel.dispatch();
180             }
181         }
182 
183         @Override
184         public void onFailure(int request, Throwable failure)
185         {
186             HttpChannelOverFCGI channel = channels.remove(request);
187             if (LOG.isDebugEnabled())
188                 LOG.debug("Request {} failure on {}: {}", request, channel, failure);
189             if (channel != null)
190             {
191                 channel.badMessage(400, failure.toString());
192             }
193         }
194     }
195 }