summaryrefslogtreecommitdiff
path: root/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiChannelHandler.java
blob: d24078221b30be95e5a93a7dc1681dd3f57d7caa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package org.jetbrains.io.fastCgi;

import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.util.containers.ConcurrentIntObjectMap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import org.jetbrains.io.Responses;
import org.jetbrains.io.SimpleChannelInboundHandlerAdapter;

import static org.jetbrains.io.fastCgi.FastCgiService.LOG;

@ChannelHandler.Sharable
public class FastCgiChannelHandler extends SimpleChannelInboundHandlerAdapter<FastCgiResponse> {
  private final ConcurrentIntObjectMap<Channel> requestToChannel;

  public FastCgiChannelHandler(ConcurrentIntObjectMap<Channel> channel) {
    requestToChannel = channel;
  }

  @Override
  protected void messageReceived(ChannelHandlerContext context, FastCgiResponse response) throws Exception {
    ByteBuf buffer = response.getData();
    Channel channel = requestToChannel.remove(response.getId());
    if (channel == null || !channel.isActive()) {
      if (buffer != null) {
        buffer.release();
      }
      return;
    }

    if (buffer == null) {
      Responses.sendStatus(HttpResponseStatus.BAD_GATEWAY, channel);
      return;
    }

    HttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buffer);
    try {
      parseHeaders(httpResponse, buffer);
      Responses.addServer(httpResponse);
      if (!HttpHeaders.isContentLengthSet(httpResponse)) {
        HttpHeaders.setContentLength(httpResponse, buffer.readableBytes());
      }
    }
    catch (Throwable e) {
      buffer.release();
      Responses.sendStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR, channel);
      LOG.error(e);
    }
    channel.writeAndFlush(httpResponse);
  }

  private static void parseHeaders(HttpResponse response, ByteBuf buffer) {
    StringBuilder builder = new StringBuilder();
    while (buffer.isReadable()) {
      builder.setLength(0);

      String key = null;
      boolean valueExpected = true;
      while (true) {
        int b = buffer.readByte();
        if (b < 0 || b == '\n') {
          break;
        }

        if (b != '\r') {
          if (valueExpected && b == ':') {
            valueExpected = false;

            key = builder.toString();
            builder.setLength(0);
            skipWhitespace(buffer);
          }
          else {
            builder.append((char)b);
          }
        }
      }

      if (builder.length() == 0) {
        // end of headers
        return;
      }

      // skip standard headers
      if (StringUtil.isEmpty(key) || StringUtilRt.startsWithIgnoreCase(key, "http") || StringUtilRt.startsWithIgnoreCase(key, "X-Accel-")) {
        continue;
      }

      String value = builder.toString();
      if (key.equalsIgnoreCase("status")) {
        response.setStatus(HttpResponseStatus.valueOf(Integer.parseInt(value.substring(0, value.indexOf(' ')))));
      }
      else if (!(key.startsWith("http") || key.startsWith("HTTP"))) {
        response.headers().add(key, value);
      }
    }
  }

  private static void skipWhitespace(ByteBuf buffer) {
    while (buffer.isReadable() && buffer.getByte(buffer.readerIndex()) == ' ') {
      buffer.skipBytes(1);
    }
  }
}