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 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); } }