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 Flusher flusher;
45      private final HttpConfiguration configuration;
46      private final ServerParser parser;
47  
48      public ServerFCGIConnection(Connector connector, EndPoint endPoint, HttpConfiguration configuration)
49      {
50          super(endPoint, connector.getExecutor());
51          this.connector = connector;
52          this.flusher = new Flusher(endPoint);
53          this.configuration = configuration;
54          this.parser = new ServerParser(new ServerListener());
55      }
56  
57      @Override
58      public void onOpen()
59      {
60          super.onOpen();
61          fillInterested();
62      }
63  
64      @Override
65      public void onFillable()
66      {
67          EndPoint endPoint = getEndPoint();
68          ByteBufferPool bufferPool = connector.getByteBufferPool();
69          ByteBuffer buffer = bufferPool.acquire(configuration.getResponseHeaderSize(), true);
70          try
71          {
72              while (true)
73              {
74                  int read = endPoint.fill(buffer);
75                  if (LOG.isDebugEnabled()) // Avoid boxing of variable 'read'
76                      LOG.debug("Read {} bytes from {}", read, endPoint);
77                  if (read > 0)
78                  {
79                      parse(buffer);
80                  }
81                  else if (read == 0)
82                  {
83                      fillInterested();
84                      break;
85                  }
86                  else
87                  {
88                      shutdown();
89                      break;
90                  }
91              }
92          }
93          catch (Exception x)
94          {
95              LOG.debug(x);
96              // TODO: fail and close ?
97          }
98          finally
99          {
100             bufferPool.release(buffer);
101         }
102     }
103 
104     private void parse(ByteBuffer buffer)
105     {
106         while (buffer.hasRemaining())
107             parser.parse(buffer);
108     }
109 
110     private void shutdown()
111     {
112         flusher.shutdown();
113     }
114 
115     private class ServerListener implements ServerParser.Listener
116     {
117         @Override
118         public void onStart(int request, FCGI.Role role, int flags)
119         {
120             // TODO: handle flags
121             HttpChannelOverFCGI channel = new HttpChannelOverFCGI(connector, configuration, getEndPoint(),
122                     new HttpTransportOverFCGI(connector.getByteBufferPool(), flusher, request), new ByteBufferQueuedHttpInput());
123             HttpChannelOverFCGI existing = channels.putIfAbsent(request, channel);
124             if (existing != null)
125                 throw new IllegalStateException();
126             if (LOG.isDebugEnabled())
127                 LOG.debug("Request {} start on {}", request, channel);
128         }
129 
130         @Override
131         public void onHeader(int request, HttpField field)
132         {
133             HttpChannelOverFCGI channel = channels.get(request);
134             if (LOG.isDebugEnabled())
135                 LOG.debug("Request {} header {} on {}", request, field, channel);
136             if (channel != null)
137                 channel.header(field);
138         }
139 
140         @Override
141         public void onHeaders(int request)
142         {
143             HttpChannelOverFCGI channel = channels.get(request);
144             if (LOG.isDebugEnabled())
145                 LOG.debug("Request {} headers on {}", request, channel);
146             if (channel != null)
147             {
148                 if (channel.headerComplete())
149                     channel.dispatch();
150             }
151         }
152 
153         @Override
154         public boolean onContent(int request, FCGI.StreamType stream, ByteBuffer buffer)
155         {
156             HttpChannelOverFCGI channel = channels.get(request);
157             if (LOG.isDebugEnabled())
158                 LOG.debug("Request {} {} content {} on {}", request, stream, buffer, channel);
159             if (channel != null)
160             {
161                 if (channel.content(buffer))
162                     channel.dispatch();
163             }
164             return false;
165         }
166 
167         @Override
168         public void onEnd(int request)
169         {
170             HttpChannelOverFCGI channel = channels.remove(request);
171             if (LOG.isDebugEnabled())
172                 LOG.debug("Request {} end on {}", request, channel);
173             if (channel != null)
174             {
175                 if (channel.messageComplete())
176                     channel.dispatch();
177             }
178         }
179 
180         @Override
181         public void onFailure(int request, Throwable failure)
182         {
183             HttpChannelOverFCGI channel = channels.remove(request);
184             if (LOG.isDebugEnabled())
185                 LOG.debug("Request {} failure on {}: {}", request, channel, failure);
186             if (channel != null)
187             {
188                 channel.badMessage(400, failure.toString());
189             }
190         }
191     }
192 }