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 <ae@cy.md> 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, DataVec; 24 import ae.utils.meta : getAttribute; 25 26 /// Holds parsed CGI meta-variables. 27 struct CGIVars 28 { 29 @("AUTH_TYPE" ) string authType; /// The CGI "AUTH_TYPE" meta-variable. 30 @("CONTENT_LENGTH" ) string contentLength; /// The CGI "CONTENT_LENGTH" meta-variable. 31 @("CONTENT_TYPE" ) string contentType; /// The CGI "CONTENT_TYPE" meta-variable. 32 @("GATEWAY_INTERFACE") string gatewayInterface; /// The CGI "GATEWAY_INTERFACE" meta-variable. 33 @("PATH_INFO" ) string pathInfo; /// The CGI "PATH_INFO" meta-variable. 34 @("PATH_TRANSLATED" ) string pathTranslated; /// The CGI "PATH_TRANSLATED" meta-variable. 35 @("QUERY_STRING" ) string queryString; /// The CGI "QUERY_STRING" meta-variable. 36 @("REMOTE_ADDR" ) string remoteAddr; /// The CGI "REMOTE_ADDR" meta-variable. 37 @("REMOTE_HOST" ) string remoteHost; /// The CGI "REMOTE_HOST" meta-variable. 38 @("REMOTE_IDENT" ) string remoteIdent; /// The CGI "REMOTE_IDENT" meta-variable. 39 @("REMOTE_USER" ) string remoteUser; /// The CGI "REMOTE_USER" meta-variable. 40 @("REQUEST_METHOD" ) string requestMethod; /// The CGI "REQUEST_METHOD" meta-variable. 41 @("SCRIPT_NAME" ) string scriptName; /// The CGI "SCRIPT_NAME" meta-variable. 42 @("SERVER_NAME" ) string serverName; /// The CGI "SERVER_NAME" meta-variable. 43 @("SERVER_PORT" ) string serverPort; /// The CGI "SERVER_PORT" meta-variable. 44 @("SERVER_PROTOCOL" ) string serverProtocol; /// The CGI "SERVER_PROTOCOL" meta-variable. 45 @("SERVER_SOFTWARE" ) string serverSoftware; /// The CGI "SERVER_SOFTWARE" meta-variable. 46 47 // SCGI vars: 48 @("REQUEST_URI" ) string requestUri; /// The SCGI "REQUEST_URI" meta-variable. 49 @("DOCUMENT_URI" ) string documentUri; /// The SCGI "DOCUMENT_URI" meta-variable. 50 @("DOCUMENT_ROOT" ) string documentRoot; /// The SCGI "DOCUMENT_ROOT" meta-variable. 51 52 /// Parse from an environment block (represented as an associate array). 53 static typeof(this) fromAA(string[string] env) 54 { 55 typeof(this) result; 56 foreach (i, ref var; result.tupleof) 57 var = env.get(getAttribute!(string, result.tupleof[i]), null); 58 return result; 59 } 60 } 61 62 /// Holds a CGI request. 63 struct CGIRequest 64 { 65 CGIVars vars; /// CGI meta-variables. 66 Headers headers; /// Request headers. 67 DataVec data; /// Request data. 68 69 /// Parse from an environment block (represented as an associate array). 70 static typeof(this) fromAA(string[string] env) 71 { 72 typeof(this) result; 73 result.vars = CGIVars.fromAA(env); 74 75 // Missing `include /etc/nginx/fastcgi_params;` in nginx? 76 enforce(result.vars.gatewayInterface, "GATEWAY_INTERFACE not set"); 77 78 enforce(result.vars.gatewayInterface == "CGI/1.1", 79 "Unknown CGI version: " ~ result.vars.gatewayInterface); 80 81 result.headers = decodeHeaders(env, result.vars.serverProtocol); 82 83 return result; 84 } 85 86 /// Extract request headers from an environment block (represented 87 /// as an associate array). 88 /// Params: 89 /// env = The environment block. 90 /// serverProtocol = The protocol (as specified in 91 /// SERVER_PROTOCOL). 92 static Headers decodeHeaders(string[string] env, string serverProtocol) 93 { 94 Headers headers; 95 auto protocolPrefix = serverProtocol.findSplit("/")[0] ~ "_"; 96 foreach (name, value; env) 97 if (name.skipOver(protocolPrefix)) 98 headers.add(name.replace("_", "-"), value); 99 return headers; 100 } 101 } 102 103 /// Subclass of `HttpRequest` for HTTP requests received via CGI. 104 class CGIHttpRequest : HttpRequest 105 { 106 CGIVars cgiVars; /// CGI meta-variables. 107 108 /// Construct the HTTP request from a CGI request. 109 this(ref CGIRequest cgi) 110 { 111 cgiVars = cgi.vars; 112 headers = cgi.headers; 113 if (cgi.vars.requestUri) 114 resource = cgi.vars.requestUri; 115 else 116 if (cgi.vars.documentUri) 117 resource = cgi.vars.documentUri; 118 else 119 resource = cgi.vars.scriptName ~ cgi.vars.pathInfo; 120 if (cgi.vars.queryString) 121 queryString = cgi.vars.queryString; 122 123 if (cgi.vars.contentType) 124 headers.require("Content-Type", cgi.vars.contentType); 125 if (cgi.vars.serverName) 126 headers.require("Host", cgi.vars.serverName); 127 if (cgi.vars.serverPort) 128 port = cgi.vars.serverPort.to!ushort; 129 method = cgi.vars.requestMethod; 130 protocol = cgi.vars.serverProtocol; 131 data = cgi.data.dup; 132 } 133 }