summaryrefslogtreecommitdiff
path: root/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiRequest.java
blob: e92d20eebffc799121091e916e5617c866ffd6f1 (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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package org.jetbrains.io.fastCgi;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.util.CharsetUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.builtInWebServer.PathInfo;
import org.jetbrains.builtInWebServer.WebServerPathToFileManager;
import org.jetbrains.io.Responses;

import java.net.InetSocketAddress;
import java.util.Locale;
import java.util.Map;

public class FastCgiRequest {
  private static final int PARAMS = 4;
  private static final int BEGIN_REQUEST = 1;
  private static final int RESPONDER = 1;
  private static final int FCGI_KEEP_CONNECTION = 1;
  private static final int STDIN = 5;
  private static final int VERSION = 1;

  private final ByteBuf buffer;
  final int requestId;

  public FastCgiRequest(int requestId, @NotNull ByteBufAllocator allocator) {
    this.requestId = requestId;

    buffer = allocator.buffer();
    writeHeader(buffer, BEGIN_REQUEST, FastCgiConstants.HEADER_LENGTH);
    buffer.writeShort(RESPONDER);
    buffer.writeByte(FCGI_KEEP_CONNECTION);
    buffer.writeZero(5);
  }

  public void writeFileHeaders(@NotNull VirtualFile file, @NotNull Project project, @NotNull CharSequence canonicalRequestPath) {
    PathInfo root = WebServerPathToFileManager.getInstance(project).getRoot(file);
    FastCgiService.LOG.assertTrue(root != null);
    addHeader("DOCUMENT_ROOT", root.getRoot().getPath());
    addHeader("SCRIPT_FILENAME", file.getPath());
    addHeader("SCRIPT_NAME", canonicalRequestPath);
  }

  public final void addHeader(@NotNull String key, @Nullable CharSequence value) {
    if (value == null) {
      return;
    }

    int keyLength = key.length();
    int valLength = value.length();
    writeHeader(buffer, PARAMS, keyLength + valLength + (keyLength < 0x80 ? 1 : 4) + (valLength < 0x80 ? 1 : 4));

    if (keyLength < 0x80) {
      buffer.writeByte(keyLength);
    }
    else {
      buffer.writeByte(0x80 | (keyLength >> 24));
      buffer.writeByte(keyLength >> 16);
      buffer.writeByte(keyLength >> 8);
      buffer.writeByte(keyLength);
    }

    if (valLength < 0x80) {
      buffer.writeByte(valLength);
    }
    else {
      buffer.writeByte(0x80 | (valLength >> 24));
      buffer.writeByte(valLength >> 16);
      buffer.writeByte(valLength >> 8);
      buffer.writeByte(valLength);
    }

    buffer.writeBytes(key.getBytes(CharsetUtil.US_ASCII));
    buffer.writeBytes(Unpooled.copiedBuffer(value, CharsetUtil.UTF_8));
  }

  public void writeHeaders(FullHttpRequest request, Channel clientChannel) {
    addHeader("REQUEST_URI", request.uri());
    addHeader("REQUEST_METHOD", request.method().name());

    InetSocketAddress remote = (InetSocketAddress)clientChannel.remoteAddress();
    addHeader("REMOTE_ADDR", remote.getAddress().getHostAddress());
    addHeader("REMOTE_PORT", Integer.toString(remote.getPort()));

    InetSocketAddress local = (InetSocketAddress)clientChannel.localAddress();
    addHeader("SERVER_SOFTWARE", Responses.getServerHeaderValue());
    addHeader("SERVER_NAME", Responses.getServerHeaderValue());

    addHeader("SERVER_ADDR", local.getAddress().getHostAddress());
    addHeader("SERVER_PORT", Integer.toString(local.getPort()));

    addHeader("GATEWAY_INTERFACE", "CGI/1.1");
    addHeader("SERVER_PROTOCOL", request.protocolVersion().text());
    addHeader("CONTENT_TYPE", request.headers().get(HttpHeaders.Names.CONTENT_TYPE));

    // PHP only, required if PHP was built with --enable-force-cgi-redirect
    addHeader("REDIRECT_STATUS", "200");

    String queryString = "";
    int queryIndex = request.uri().indexOf('?');
    if (queryIndex != -1) {
      queryString = request.uri().substring(queryIndex + 1);
    }
    addHeader("QUERY_STRING", queryString);

    addHeader("CONTENT_LENGTH", String.valueOf(request.content().readableBytes()));

    for (Map.Entry<String, String> entry : request.headers()) {
      addHeader("HTTP_" + entry.getKey().replace('-', '_').toUpperCase(Locale.ENGLISH), entry.getValue());
    }
  }

  final void writeToServerChannel(ByteBuf content, Channel fastCgiChannel) {
    writeHeader(buffer, PARAMS, 0);
    fastCgiChannel.write(buffer);

    if (content.isReadable()) {
      ByteBuf headerBuffer = fastCgiChannel.alloc().buffer(FastCgiConstants.HEADER_LENGTH, FastCgiConstants.HEADER_LENGTH);
      writeHeader(headerBuffer, STDIN, content.readableBytes());
      fastCgiChannel.write(headerBuffer);

      fastCgiChannel.write(content);

      headerBuffer = fastCgiChannel.alloc().buffer(FastCgiConstants.HEADER_LENGTH, FastCgiConstants.HEADER_LENGTH);
      writeHeader(headerBuffer, STDIN, 0);
      fastCgiChannel.write(headerBuffer);
    }
    else {
      content.release();
    }

    fastCgiChannel.flush();
  }

  private void writeHeader(ByteBuf buffer, int type, int length) {
    buffer.writeByte(VERSION);
    buffer.writeByte(type);
    buffer.writeShort(requestId);
    buffer.writeShort(length);
    buffer.writeZero(2);
  }
}