import std/options import std/os import std/posix import std/streams import std/strutils import extern/stdio import io/posixstream import loader/connecterror import loader/headers import loader/loaderhandle import loader/request import types/formdata import types/opt import types/url import utils/twtstr proc putMappedURL(url: URL) = putEnv("MAPPED_URI_SCHEME", url.scheme) putEnv("MAPPED_URI_USERNAME", url.username) putEnv("MAPPED_URI_PASSWORD", url.password) putEnv("MAPPED_URI_HOST", url.hostname) putEnv("MAPPED_URI_PORT", url.port) putEnv("MAPPED_URI_PATH", url.path.serialize()) putEnv("MAPPED_URI_QUERY", url.query.get("")) proc setupEnv(cmd, scriptName, pathInfo, requestURI, libexecPath: string, request: Request, contentLen: int, prevURL: URL) = let url = request.url putEnv("SERVER_SOFTWARE", "Chawan") putEnv("SERVER_PROTOCOL", "HTTP/1.0") putEnv("SERVER_NAME", "localhost") putEnv("SERVER_PORT", "80") putEnv("REMOTE_HOST", "localhost") putEnv("REMOTE_ADDR", "127.0.0.1") putEnv("GATEWAY_INTERFACE", "CGI/1.1") putEnv("SCRIPT_NAME", scriptName) putEnv("SCRIPT_FILENAME", cmd) putEnv("REQUEST_URI", requestURI) putEnv("REQUEST_METHOD", $request.httpMethod) putEnv("CHA_LIBEXEC_DIR", libexecPath) var headers = "" for k, v in request.headers: headers &= k & ": " & v & "\r\n" putEnv("REQUEST_HEADERS", headers) if prevURL != nil: putMappedURL(prevURL) if pathInfo != "": putEnv("PATH_INFO", pathInfo) if url.query.isSome: putEnv("QUERY_STRING", url.query.get) if request.httpMethod == HTTP_POST: if request.multipart.isSome: putEnv("CONTENT_TYPE", request.multipart.get.getContentType()) else: putEnv("CONTENT_TYPE", request.headers.getOrDefault("Content-Type", "")) putEnv("CONTENT_LENGTH", $contentLen) if "Cookie" in request.headers: putEnv("HTTP_COOKIE", request.headers["Cookie"]) if request.referer != nil: putEnv("HTTP_REFERER", $request.referer) if request.proxy != nil: putEnv("ALL_PROXY", $request.proxy) type ControlResult = enum RESULT_CONTROL_DONE, RESULT_CONTROL_CONTINUE, RESULT_ERROR proc handleFirstLine(handle: LoaderHandle, line: string, headers: Headers, status: var int): ControlResult = let k = line.until(':') if k.len == line.len: # invalid handle.sendResult(ERROR_CGI_MALFORMED_HEADER) return RESULT_ERROR let v = line.substr(k.len + 1).strip() if k.equalsIgnoreCase("Status"): handle.sendResult(0) # success status = parseInt32(v).get(0) return RESULT_CONTROL_CONTINUE if k.equalsIgnoreCase("Cha-Control"): if v.startsWithIgnoreCase("Connected"): handle.sendResult(0) # success return RESULT_CONTROL_CONTINUE elif v.startsWithIgnoreCase("ConnectionError"): let errs = v.split(' ') if errs.len <= 1: handle.sendResult(ERROR_CGI_INVALID_CHA_CONTROL) else: let fb = int32(ERROR_CGI_INVALID_CHA_CONTROL) let code = int(parseInt32(errs[1]).get(fb)) var message = "" if errs.len > 2: message &= errs[2] for i in 3 ..< errs.len: message &= ' ' message &= errs[i] handle.sendResult(code, message) return RESULT_ERROR elif v.startsWithIgnoreCase("ControlDone"): return RESULT_CONTROL_DONE handle.sendResult(ERROR_CGI_INVALID_CHA_CONTROL) return RESULT_ERROR handle.sendResult(0) # success headers.add(k, v) return RESULT_CONTROL_DONE proc handleControlLine(handle: LoaderHandle, line: string, headers: Headers, status: var int): ControlResult = let k = line.until(':') if k.len == line.len: # invalid return RESULT_ERROR let v = line.substr(k.len + 1).strip() if k.equalsIgnoreCase("Status"): status = parseInt32(v).get(0) return RESULT_CONTROL_CONTINUE if k.equalsIgnoreCase("Cha-Control"): if v.startsWithIgnoreCase("ControlDone"): return RESULT_CONTROL_DONE return RESULT_ERROR headers.add(k, v) return RESULT_CONTROL_DONE # returns false if transfer was interrupted proc handleLine(handle: LoaderHandle, line: string, headers: Headers) = let k = line.until(':') if k.len == line.len: # invalid return let v = line.substr(k.len + 1).strip() headers.add(k, v)