Home modules.gotpike.org
Username: Password: [Create Account]
[Forgot Password?]

Modules

ADT
Database
GTK2
GUI
IP
PiJAX
Public
Sql
Stdio
Subversion
System
Tools
Xosd
lua
v4l2
wx

Recent Changes

Public.Parser.XML2 1.50
Public.ZeroMQ 1.1
Public.Template.Mustache 1.0
Public.Protocols.XMPP 1.4
Sql.Provider.jdbc 1.0

Popular Downloads

Public.Parser.JSON2 1.0
Public.Parser.JSON 0.2
GTK2 2.23
Public.Web.FCGI 1.8
Public.Parser.XML2 1.48


Module Information
Public.Web.FCGI
Viewing contents of Public_Web_FCGI-1.3/FCGI.cmod

/*
 * Copyright (c) 2005, 2006 Adam Montague 
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "util.h"
#include 

/* This module is a wrapper around the fcgiapp functions, meaning you can't
   use this module to make a CGI, only a real fastcgi app.
   Also, I have not implimented the following functions as I don't know what
   use they are, if any.
FCGX_Free - Free a requests memory, both finish() and accept() do this for you though
FCGX_SetExitStatus - set what the exit status would be if it were a cgi (who cares?)
FCGX_GetChar - read a single character and allow it to be unget later (what for?)
FCGX_UnGetChar - stick a single character back onto a stream you used GetChar on
*/

/*! @module Public
 */

/*! @module Web
 */

/*! @module FCGI
 */

static int shutdown_pending;

EXTRA
{
	if (FCGX_Init() != 0)
		Pike_error("FCGI initialization failed\n");
	shutdown_pending = 0;
}

/*! @decl int open_socket(string path, int backlog)
 *!   Creates a socket for fastcgi communication with a webserver.
 *!   Returns the file descriptor to be used when creating a FCGI.FCGI()
 *!   object, or -1 on failure.
 *!
 *! @param path
 *!   The filename of the unix domain socket (or named pipe on WinNT),
 *!   or an optional host, followed by a colon and a port number for a TCP socket.
 *!
 *! @param backlog
 *!   The size of the queue used in the listen() call for this socket
 */
PIKEFUN int open_socket(string path, int backlog)
{
	RETURN(FCGX_OpenSocket(path->str, backlog));
}

/*! @decl void shutdown()
 *!   Stop accepting new connections.  Any calls to accept() after shutdown() has
 *!   been called will fail.  This function is signal handler safe.
 */
PIKEFUN void shutdown()
{
	shutdown_pending = 1;
	FCGX_ShutdownPending();
}

/*! @decl string decode_url(string s)
 *!   Decodes a urlencoded string, replacing "+" with " ", and converting
 *!   %XX hex codes to their appropriate values.  Any invalid %XX codes
 *!   are removed from the string, while saving as much of the string as
 *!   possible.  For example, "%2r" will become "r".
 *!
 *! @param s
 *!   The urlencoded string.
 */
PIKEFUN string decode_url(string s)
{
	static const char *hex = "0123456789abcdef";
	struct pike_string *ret = begin_shared_string(s->len);
	const char *src = s->str;
	char *dst = ret->str;
	char *tmp;
	char *max = s->str + s->len;

	while (src < max) {
		if (*src == '%') {
			src++;
			if (src < max && (tmp = strchr(hex, tolower(*src))) != NULL) {
				*dst = (tmp - hex) * 16;
				src++;
				if (src < max && (tmp = strchr(hex, tolower(*src))) != NULL) {
					*dst += tmp - hex;
					src++;
					dst++;
				}
			}
		} else {
			*dst = *src == '+' ? ' ' : *src;
			src++;
			dst++;
		}
	}
	RETURN(end_and_resize_shared_string(ret, dst - ret->str));
}

/*! @decl string encode_url(string s)
 *!   Encodes a urlencoded string, replacing " " with "+", and converting
 *!   characters that cannot be in urls to their appropriate %XX hex codes
 *!   according to RFC 1738.
 *!
 *! @param s
 *!   The string to encode.
 */
PIKEFUN string encode_url(string s)
{
	static const char *allowed = "$&+,/:;=?@-_.!*'()";
	static const char *hex = "0123456789abcdef";
	struct pike_string *ret = begin_shared_string(s->len * 3);	// max possible encoded size
	const char *src = s->str;
	char *dst = ret->str;
	char *max = s->str + s->len;

	while (src < max) {
		if (*src == ' ') {
			*dst = '+';
			dst++;
		} else if (!isalnum(*src) || strchr(allowed, *src) != NULL) {
			*dst = '%';
			*dst++ = hex[*src / 16];
			*dst++ = hex[*src % 16];
			dst++;
		} else {
			*dst = *src;
			dst++;
		}
		src++;
	}
	RETURN(end_and_resize_shared_string(ret, dst - ret->str));
}

/*! @class FCGI
 */
PIKECLASS FCGI
{
	CVAR FCGX_Request *request;
	CVAR int status;

/*! @decl mapping(string:string) env
 *!   A mapping of all HTTP and server variables supplied for this FCGI request.
 */
	PIKEVAR mapping(string:string) env;

	INIT
	{
		if ((THIS->request = malloc(sizeof(FCGX_Request))) == NULL)
			Pike_error("Memory allocation failed in INIT!\n");

		THIS->env = NULL;
		THIS->status = 0;
	}

	EXIT
	{
		if (THIS->request != NULL) {
			FCGX_Free(THIS->request, 0);
			THIS->request = NULL;
		}
		if (THIS->env != NULL) {
			free_mapping(THIS->env);
			THIS->env = NULL;
		}
	}

/*! @decl void create(int sock)
 *!   Creates a new fastcgi request object
 *!
 *! @param sock
 *!   The file descriptor to accept on, as returned from open_socket()
 */
	PIKEFUN void create(int sock)
	{
		if (FCGX_InitRequest(THIS->request, sock, 0) != 0)
			Pike_error("Request initialization failed\n");

		pop_n_elems(args);
	}

/*! @decl int accept()
 *!   Frees the resources allocated to, and closes any previous connection,
 *!   then accepts a new connection.  This function blocks indefinately until
 *!   a new connection is ready, and returns 0 on failure or 1 on success.
 */
	PIKEFUN int accept()
	{
		int ret, i;
		char **p;
		char *key, *value;
		fd_set read_fds;
		struct timeval tv;

		i = 0;
		ret = 0;

		if (shutdown_pending) {
			RETURN(0);
		}
		while (ret < 1) {
			/* apparently some select()s alter tv? */
			tv.tv_usec = 100000;
			tv.tv_sec = 0;
			FD_ZERO(&read_fds);
			FD_SET(THIS->request->listen_sock, &read_fds);
			THREADS_ALLOW();
			ret = select(1, &read_fds, NULL, NULL, &tv);
			THREADS_DISALLOW();
			if (ret == -1 || shutdown_pending) {
				RETURN(0);
			}
		}
		ret = FCGX_Accept_r(THIS->request);
		
		if (ret != 0)
			RETURN(0);

		if (THIS->env != NULL)
			free_mapping(THIS->env);

		/* loop over the array of strings, and split it into a mapping */
		for (p = THIS->request->envp; *p != NULL; p++) {
			if ((key = strdup(*p)) == NULL)
				Pike_error("Memory allocation failed!\n");
			value = strchr(key, '=');
			*value = NULL;
			push_text(key);
			push_text(value + 1);
			free(key);
			key = NULL;
			value = NULL;
			i += 2;
		}

		f_aggregate_mapping(i);
		THIS->env = Pike_sp[-1].u.mapping;
		add_ref(THIS->env);
		pop_stack();
		THIS->status = 1;
		RETURN(1);
	}

/*! @decl void finish()
 *!   Frees the resources allocated to this connection, and closes it.
 *!   This function flushes all buffered data to the client's web browser
 *!   and must be called when done with a request before calling accept()
 *!   again.
 */
	PIKEFUN void finish()
	{
		THIS->status = 0;
		FCGX_Finish_r(THIS->request);
	}

/*! @decl int write(string data, mixed ... extras)
 *!   Write a string to the current connection's standard output (someone's web browser).
 *!   This function takes the same aruments and works just like predef::write
 */
	PIKEFUN int real_write(string data, mixed ... extras)
	{
		if (!THIS->status)
			Pike_error("Must call accept before write\n");

		/* if we have format and extras, then make it into a string */
		if (args > 1) {
			f_sprintf(args);
			args = 1;
		}

		RETURN(FCGX_PutStr(Pike_sp[0 - args].u.string->str, Pike_sp[0 - args].u.string->len, THIS->request->out));
	}

/*! @decl int werror(string data, mixed ... extras)
 *!   Write a string to the current connection's standard error.
 *!   This function takes the same aruments and works just like predef::werror
 */
	PIKEFUN int werror(string data, mixed ... extras)
	{
		if (!THIS->status)
			Pike_error("Must call accept before werror\n");

		/* if we have format and extras, then make it into a string */
		if (args > 1) {
			f_sprintf(args);
			args = 1;
		}

		RETURN(FCGX_PutStr(Pike_sp[0 - args].u.string->str, Pike_sp[0 - args].u.string->len, THIS->request->err));
	}

/*! @decl string read(int len)
 *!   Read data from the current connection's input stream.
 *!
 *! @param len
 *!   The number of bytes to read.  If fewer than len bytes are available, read will
 *!   return all the available data.
 */
	PIKEFUN string read(int len)
	{
		struct pike_string *ret;
		int i;

		if (!THIS->status)
			Pike_error("Must call accept before read\n");

		ret = begin_shared_string(len);
		i = FCGX_GetStr(ret->str, len, THIS->request->in);
		RETURN(end_and_resize_shared_string(ret, i));
	}

/*! @decl string gets()
 *!   Read a single line (including trailing newline) from the current connection's
 *!   input stream.
 */
	PIKEFUN string gets()
	{
		struct pike_string *ret;
		int i;
		char *newp, *p;

		if (!THIS->status)
			Pike_error("Must call accept before gets\n");

		i = 8192;
		p = malloc(i);
		if (p == NULL)
			Pike_error("Memory allocation failed!\n");

		if (FCGX_GetLine(p, i, THIS->request->in) == NULL) {
			free(p);
			RETURN(make_shared_string(""));
		}
		if (p[strlen(p) - 1] == '\n') {
			ret = make_shared_string(p);
			free(p);
			RETURN(ret);
		}

		while (FCGX_HasSeenEOF(THIS->request->in) == 0) {
			i += 8192;
			if ((newp = realloc(p, i)) == NULL) {
				free(p);
				Pike_error("Memory allocation failed!\n");
			}
			p = newp;
			if (FCGX_GetLine(p + (i - 8193), i, THIS->request->in) == NULL)
				break;
			if (p[strlen(p) - 2] == '\n')
				break;
		}
		ret = make_shared_string(p);
		free(p);
		RETURN(ret);
	}

/*! @decl int flush()
 *!   Flush the output buffer of the current connection manually.  The buffer is flushed
 *!   automatically when finish or accept are called.  Returns 1 on success or 0 on failure.
 */
	PIKEFUN int flush()
	{
		if (!THIS->status)
			Pike_error("Must call accept before flush\n");

		if (FCGX_FFlush(THIS->request->out) == -1)
			RETURN(0);
		else
			RETURN(1);
	}

/*! @decl int eof()
 *!   Returns 1 if the current connection's input stream has been
 *!   read until EOF, or 0 if it has not.
 */
	PIKEFUN int eof()
	{
		if (!THIS->status)
			Pike_error("Must call accept before eof\n");

		if (FCGX_HasSeenEOF(THIS->request->in) == 0)
			RETURN(0);
		else
			RETURN(1);
	}

/*! @decl int errno()
 *!   Returns the most recent error that has occured on the current connection's
 *!   input stream, or 0 if no error has occured.
 */
	PIKEFUN int errno()
	{
		if (!THIS->status)
			Pike_error("Must call accept before errno\n");

		RETURN(FCGX_GetError(THIS->request->in));
	}

/*! @decl void clear_errno()
 *!   Clears any error or EOF indications from the current connection's input stream.
 */
	PIKEFUN void clear_errno()
	{
		if (!THIS->status)
			Pike_error("Must call accept before clear_errno\n");

		FCGX_ClearError(THIS->request->in);
	}

/*! @decl int start_filter_data()
 *!   Sets the current connection's input stream to read from the FCGI_DATA area.
 *!   You must read all data from the input stream before calling this function.
 *!   Returns 1 on success, or 0 if an error occured.
 */
	PIKEFUN int start_filter_data()
	{
		if (!THIS->status)
			Pike_error("Must call accept before start_filter_data\n");

		if (FCGX_StartFilterData(THIS->request->in) == 0)
			RETURN(1);
		else
			RETURN(0);
	}
}

/*! @endclass
 */

/*! @endmodule
 */

/*! @endmodule
 */

/*! @endmodule
 */


gotpike.org | Copyright © 2004 - 2019 | Pike is a trademark of Department of Computer and Information Science, Linköping University