1 /** 2 * Common CGI declarations. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.net.http.cgi.common; 15 16 import std.algorithm.searching : skipOver, findSplit; 17 import std.array : replace; 18 import std.conv; 19 import std.exception : enforce; 20 21 import ae.net.http.common : HttpRequest; 22 import ae.net.ietf.headers : Headers; 23 import ae.sys.data : Data; 24 import ae.utils.meta : getAttribute; 25 26 /// CGI meta-variables 27 struct CGIVars 28 { 29 @("AUTH_TYPE" ) string authType; 30 @("CONTENT_LENGTH" ) string contentLength; 31 @("CONTENT_TYPE" ) string contentType; 32 @("GATEWAY_INTERFACE") string gatewayInterface; 33 @("PATH_INFO" ) string pathInfo; 34 @("PATH_TRANSLATED" ) string pathTranslated; 35 @("QUERY_STRING" ) string queryString; 36 @("REMOTE_ADDR" ) string remoteAddr; 37 @("REMOTE_HOST" ) string remoteHost; 38 @("REMOTE_IDENT" ) string remoteIdent; 39 @("REMOTE_USER" ) string remoteUser; 40 @("REQUEST_METHOD" ) string requestMethod; 41 @("SCRIPT_NAME" ) string scriptName; 42 @("SERVER_NAME" ) string serverName; 43 @("SERVER_PORT" ) string serverPort; 44 @("SERVER_PROTOCOL" ) string serverProtocol; 45 @("SERVER_SOFTWARE" ) string serverSoftware; 46 47 // SCGI vars: 48 @("REQUEST_URI" ) string requestUri; 49 @("DOCUMENT_URI" ) string documentUri; 50 @("DOCUMENT_ROOT" ) string documentRoot; 51 52 static typeof(this) fromAA(string[string] env) 53 { 54 typeof(this) result; 55 foreach (i, ref var; result.tupleof) 56 var = env.get(getAttribute!(string, result.tupleof[i]), null); 57 return result; 58 } 59 } 60 61 struct CGIRequest 62 { 63 CGIVars vars; 64 Headers headers; 65 Data[] data; 66 67 static typeof(this) fromAA(string[string] env) 68 { 69 typeof(this) result; 70 result.vars = CGIVars.fromAA(env); 71 72 // Missing `include /etc/nginx/fastcgi_params;` in nginx? 73 enforce(result.vars.gatewayInterface, "GATEWAY_INTERFACE not set"); 74 75 enforce(result.vars.gatewayInterface == "CGI/1.1", 76 "Unknown CGI version: " ~ result.vars.gatewayInterface); 77 78 result.headers = decodeHeaders(env, result.vars.serverProtocol); 79 80 return result; 81 } 82 83 static Headers decodeHeaders(string[string] env, string serverProtocol) 84 { 85 Headers headers; 86 auto protocolPrefix = serverProtocol.findSplit("/")[0] ~ "_"; 87 foreach (name, value; env) 88 if (name.skipOver(protocolPrefix)) 89 headers.add(name.replace("_", "-"), value); 90 return headers; 91 } 92 } 93 94 class CGIHttpRequest : HttpRequest 95 { 96 CGIVars cgiVars; 97 98 this(ref CGIRequest cgi) 99 { 100 cgiVars = cgi.vars; 101 headers = cgi.headers; 102 if (cgi.vars.requestUri) 103 resource = cgi.vars.requestUri; 104 else 105 if (cgi.vars.documentUri) 106 resource = cgi.vars.documentUri; 107 else 108 resource = cgi.vars.scriptName ~ cgi.vars.pathInfo; 109 if (cgi.vars.queryString) 110 queryString = cgi.vars.queryString; 111 112 if (cgi.vars.contentType) 113 headers.require("Content-Type", cgi.vars.contentType); 114 if (cgi.vars.serverName) 115 headers.require("Host", cgi.vars.serverName); 116 if (cgi.vars.serverPort) 117 port = cgi.vars.serverPort.to!ushort; 118 method = cgi.vars.requestMethod; 119 protocol = cgi.vars.serverProtocol; 120 data = cgi.data; 121 } 122 }