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