diff options
author | Dmitry Stogov <dmitry@php.net> | 2006-02-02 08:17:23 +0000 |
---|---|---|
committer | Dmitry Stogov <dmitry@php.net> | 2006-02-02 08:17:23 +0000 |
commit | 7e7fcc3a078029b06a23e7cc70cde935575df889 (patch) | |
tree | a91c4a827dd0441217e6993538f49304f7fb973c /sapi/cgi/fastcgi.c | |
parent | 9774b6d3e63526f61ba98d0f53b3dd2304473980 (diff) | |
download | php-git-7e7fcc3a078029b06a23e7cc70cde935575df889.tar.gz |
Reimplement FastCGI interface
Due to licensing restrictions that actually don't allow anybody to use
the libfcgi code without prior approval from Open Market, which is
impossible to obtain - we decided to reimplement this interface from
scratch. The result is actually slightly faster, and more importantly,
clear of any copyright issues.
Diffstat (limited to 'sapi/cgi/fastcgi.c')
-rw-r--r-- | sapi/cgi/fastcgi.c | 908 |
1 files changed, 908 insertions, 0 deletions
diff --git a/sapi/cgi/fastcgi.c b/sapi/cgi/fastcgi.c new file mode 100644 index 0000000000..f2ba15982e --- /dev/null +++ b/sapi/cgi/fastcgi.c @@ -0,0 +1,908 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "fastcgi.h" +#include "php.h" + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> + +#ifdef _WIN32 + +#include <windows.h> + + typedef unsigned int size_t; + + struct sockaddr_un { + short sun_family; + char sun_path[MAXPATHLEN]; + }; + + static HANDLE fcgi_accept_mutex = INVALID_HANDLE_VALUE; + static int is_impersonate = 0; + +#define FCGI_LOCK(fd) \ + if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \ + DWORD ret; \ + while ((ret = WaitForSingleObject(fcgi_accept_mutex, 1000)) == WAIT_TIMEOUT) { \ + if (in_shutdown) return -1; \ + } \ + if (ret == WAIT_FAILED) { \ + fprintf(stderr, "WaitForSingleObject() failed\n"); \ + return -1; \ + } \ + } + +#define FCGI_UNLOCK(fd) \ + if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \ + ReleaseMutex(fcgi_accept_mutex); \ + } + +#else + +# include <sys/types.h> +# include <sys/stat.h> +# include <unistd.h> +# include <fcntl.h> +# include <sys/socket.h> +# include <sys/un.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# include <netdb.h> +# include <sys/signal.h> + +# ifndef HAVE_SOCKLEN_T + typedef unsigned int socklen_t; +# endif + +# ifdef USE_LOCKING +# define FCGI_LOCK(fd) \ + do { \ + struct flock lock; \ + lock.l_type = F_WRLCK; \ + lock.l_start = 0; \ + lock.l_whence = SEEK_SET; \ + lock.l_len = 0; \ + if (fcntl(fd, F_SETLKW, &lock) != -1) { \ + break; \ + } else if (errno != EINTR || in_shutdown) { \ + return -1; \ + } \ + } while (1) + +# define FCGI_UNLOCK(fd) \ + do { \ + int orig_errno = errno; \ + while (1) { \ + struct flock lock; \ + lock.l_type = F_UNLCK; \ + lock.l_start = 0; \ + lock.l_whence = SEEK_SET; \ + lock.l_len = 0; \ + if (fcntl(fd, F_SETLK, &lock) != -1) { \ + break; \ + } else if (errno != EINTR) { \ + return -1; \ + } \ + } \ + errno = orig_errno; \ + } while (0) +# else +# define FCGI_LOCK(fd) +# define FCGI_UNLOCK(fd) +# endif + +#endif + +typedef union _sa_t { + struct sockaddr sa; + struct sockaddr_un sa_unix; + struct sockaddr_in sa_inet; +} sa_t; + +typedef struct _fcgi_mgmt_rec { + char* name; + char val; +} fcgi_mgmt_rec; + +static const fcgi_mgmt_rec fcgi_mgmt_vars[] = { + {"FCGI_MAX_CONNS", 1}, + {"FCGI_MAX_REQS", 1}, + {"FCGI_MPXS_CONNS", 0} +}; + + +static int is_initialized = 0; +static int is_fastcgi = 0; +static int in_shutdown = 0; + +static inline char* fcgi_strndup(const char *str, int len) +{ + char *s = malloc(len+1); + memcpy(s, str, len+1); + return s; +} + +#ifdef _WIN32 + +static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg) +{ + HANDLE shutdown_event = (HANDLE) arg; + WaitForSingleObject(shutdown_event, INFINITE); + in_shutdown = 1; + return 0; +} + +#else + +static void fcgi_signal_handler(int signo) +{ + if (signo == SIGUSR1) { + in_shutdown = 1; + } +} + +#endif + +int fcgi_init(void) +{ + if (!is_initialized) { +#ifdef _WIN32 +# if 0 + /* TODO: Support for TCP sockets */ + WSADATA wsaData; + + if (WSAStartup(MAKEWORD(2,0), &wsaData)) { + fprintf(stderr, "Error starting Windows Sockets. Error: %d", WSAGetLastError()); + return 0; + } +# endif + is_initialized = 1; + + if ((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) && + (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) && + (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE)) { + char *str; + DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT; + HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE); + + SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL); + + str = getenv("_FCGI_SHUTDOWN_EVENT_"); + if (str != NULL) { + HANDLE shutdown_event = (HANDLE) atoi(str); + if (!CreateThread(NULL, 0, fcgi_shutdown_thread, + shutdown_event, 0, NULL)) { + return -1; + } + } + str = getenv("_FCGI_MUTEX_"); + if (str != NULL) { + fcgi_accept_mutex = (HANDLE) atoi(str); + } + return is_fastcgi = 1; + } else { + return is_fastcgi = 0; + } +#else + sa_t sa; + socklen_t len = sizeof(sa); + + is_initialized = 1; + errno = 0; + if (getpeername(0, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) { + struct sigaction new_sa, old_sa; + + sigemptyset(&new_sa.sa_mask); + new_sa.sa_flags = 0; + new_sa.sa_handler = fcgi_signal_handler; + sigaction(SIGUSR1, &new_sa, NULL); + sigaction(SIGPIPE, NULL, &old_sa); + if (old_sa.sa_handler == SIG_DFL) { + sigaction(SIGPIPE, &new_sa, NULL); + } + + return is_fastcgi = 1; + } else { + return is_fastcgi = 0; + } +#endif + } + return is_fastcgi; +} + + +int fcgi_is_fastcgi(void) +{ + if (!is_initialized) { + return fcgi_init(); + } else { + return is_fastcgi; + } +} + +int fcgi_listen(const char *path, int backlog) +{ +#ifdef _WIN32 + /* TODO: Support for manual binding on TCP sockets (php -b <port>) */ + return -1; +#else + char *s; + int tcp = 0; + char host[MAXPATHLEN]; + short port = 0; + int listen_socket; + sa_t sa; + socklen_t sa_len; + + if ((s = strchr(path, ':'))) { + port = atoi(s+1); + if (port != 0 && (s-path) < MAXPATHLEN) { + strncpy(host, path, s-path); + host[s-path] = '\0'; + tcp = 1; + } + } + + /* Prepare socket address */ + if (tcp) { + memset(&sa.sa_inet, 0, sizeof(sa.sa_inet)); + sa.sa_inet.sin_family = AF_INET; + sa.sa_inet.sin_port = htons(port); + sa_len = sizeof(sa.sa_inet); + + if (!*host || !strncmp(host, "*", sizeof("*")-1)) { + sa.sa_inet.sin_addr.s_addr = htonl(INADDR_ANY); + } else { + sa.sa_inet.sin_addr.s_addr = inet_addr(host); + if (sa.sa_inet.sin_addr.s_addr == INADDR_NONE) { + struct hostent *hep; + + hep = gethostbyname(host); + if (!hep || hep->h_addrtype != AF_INET || !hep->h_addr_list[0]) { + fprintf(stderr, "Cannot resolve host name '%s'!\n", host); + return -1; + } else if (hep->h_addr_list[1]) { + fprintf(stderr, "Host '%s' has multiple addresses. You must choose one explicitly!\n", host); + return -1; + } + sa.sa_inet.sin_addr.s_addr = ((struct in_addr*)hep->h_addr_list[0])->s_addr; + } + } + } else { + int path_len = strlen(path); + + if (path_len >= sizeof(sa.sa_unix.sun_path)) { + fprintf(stderr, "Listening socket's path name is too long.\n"); + return -1; + } + + memset(&sa.sa_unix, 0, sizeof(sa.sa_unix)); + sa.sa_unix.sun_family = AF_UNIX; + memcpy(sa.sa_unix.sun_path, path, path_len + 1); + sa_len = sizeof(sa.sa_unix.sun_family) + path_len; +#ifdef HAVE_SOCKADDR_UN_SUN_LEN + sa_len += sizeof(sa.sa_unix.sun_len) + 1; + sa.sa_unix.sun_len = sa_len; +#endif + unlink(path); + } + + /* Create, bind socket and start listen on it */ + if ((listen_socket = socket(sa.sa.sa_family, SOCK_STREAM, 0)) < 0 || + bind(listen_socket, (struct sockaddr *) &sa, sa_len) < 0 || + listen(listen_socket, backlog) < 0) { + + fprintf(stderr, "Cannot bind/listen socket - [%d] %s.\n",errno, strerror(errno)); + return -1; + } + + if (!tcp) { + chmod(path, 0777); + } + + if (!is_initialized) { + fcgi_init(); + } + is_fastcgi = 1; + return listen_socket; +#endif +} + +void fcgi_init_request(fcgi_request *req, int listen_socket) +{ + memset(req, 0, sizeof(fcgi_request)); + req->listen_socket = listen_socket; + req->fd = -1; + req->id = -1; + + req->in_len = 0; + req->in_pad = 0; + + req->out_hdr = NULL; + req->out_pos = req->out_buf; +} + +static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count) +{ + int ret; + size_t n = 0; + + do { + ret = write(req->fd, ((char*)buf)+n, count-n); + if (ret > 0) { + n += ret; + } else if (ret <= 0 && errno != 0 && errno != EINTR) { + return ret; + } + } while (n != count); + return n; +} + +static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count) +{ + int ret; + size_t n = 0; + + do { + ret = read(req->fd, ((char*)buf)+n, count-n); + if (ret > 0) { + n += ret; + } else if (ret == 0 && errno == 0) { + return n; + } else if (ret <= 0 && errno != 0 && errno != EINTR) { + return ret; + } + } while (n != count); + return n; +} + +static inline int fcgi_make_header(fcgi_header *hdr, fcgi_request_type type, int req_id, int len) +{ + int pad = ((len + 7) & ~7) - len; + + hdr->contentLengthB0 = (unsigned char)(len & 0xff); + hdr->contentLengthB1 = (unsigned char)((len >> 8) & 0xff); + hdr->paddingLength = (unsigned char)pad; + hdr->requestIdB0 = (unsigned char)(req_id & 0xff); + hdr->requestIdB1 = (unsigned char)((req_id >> 8) & 0xff); + hdr->reserved = 0; + hdr->type = type; + hdr->version = FCGI_VERSION_1; + return pad; +} + +static int fcgi_get_params(fcgi_request *req, unsigned char *p, unsigned char *end, int n) +{ + int name_len, val_len; + char *s; + + while (p < end) { + name_len = *p++; + if (name_len >= 128) { + name_len = ((name_len & 0x7f) << 24); + name_len |= (*p++ << 16); + name_len |= (*p++ << 8); + name_len |= *p++; + } + val_len = *p++; + if (val_len >= 128) { + val_len = ((val_len & 0x7f) << 24); + val_len |= (*p++ << 16); + val_len |= (*p++ << 8); + val_len |= *p++; + } + req->env[n] = s = malloc(name_len + val_len + 2); + memcpy(s, p, name_len); + p += name_len; + s[name_len] = '='; + memcpy(s+name_len+1, p, val_len); + p += val_len; + s[name_len+1+val_len] = '\0'; + n++; + if (n > sizeof(req->env)/sizeof(req->env[0])) { + /* TODO: to many environment variables */ + return n; + } + } + return n; +} + +static int fcgi_read_request(fcgi_request *req) +{ + fcgi_header hdr; + int len, padding; + int n = 1; + char *s; + unsigned char buf[FCGI_MAX_LENGTH+8]; + + req->keep = 0; + req->in_len = 0; + req->out_hdr = NULL; + req->out_pos = req->out_buf; + memset(req->env, 0, sizeof(req->env)); + + if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || + hdr.version < FCGI_VERSION_1) { + return 0; + } + + len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; + padding = hdr.paddingLength; + + while (hdr.type == FCGI_STDIN && len == 0) { + if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || + hdr.version < FCGI_VERSION_1) { + return 0; + } + + len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; + padding = hdr.paddingLength; + } + + req->id = (hdr.requestIdB1 << 8) + hdr.requestIdB0; + + if (hdr.type == FCGI_BEGIN_REQUEST && len == sizeof(fcgi_begin_request)) { + if (safe_read(req, buf, len+padding) != len+padding) { + return 0; + } + + req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN); + switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) { + case FCGI_RESPONDER: + req->env[0] = fcgi_strndup("FCGI_ROLE=RESPONDER", sizeof("FCGI_ROLE=RESPONDER")-1); + break; + case FCGI_AUTHORIZER: + req->env[0] = fcgi_strndup("FCGI_ROLE=AUTHORIZER", sizeof("FCGI_ROLE=AUTHORIZER")-1); + break; + case FCGI_FILTER: + req->env[0] = fcgi_strndup("FCGI_ROLE=FILTER", sizeof("FCGI_ROLE=FILTER")-1); + break; + default: + return 0; + } + + if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || + hdr.version < FCGI_VERSION_1) { + return 0; + } + + len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; + padding = hdr.paddingLength; + + while (hdr.type == FCGI_PARAMS && len > 0) { + if (safe_read(req, buf, len+padding) != len+padding) { + req->keep = 0; + return 0; + } + n = fcgi_get_params(req, buf, buf+len, n); + + if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || + hdr.version < FCGI_VERSION_1) { + req->keep = 0; + return 0; + } + len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; + padding = hdr.paddingLength; + } + } else if (hdr.type == FCGI_GET_VALUES) { + int i, j; + int name_len; + unsigned char *p = buf + sizeof(fcgi_header); + + if (safe_read(req, buf, len+padding) != len+padding) { + return 0; + } + n = fcgi_get_params(req, buf, buf+len, 0); + for (i = 0; i < n; i++) { + if ((s = strchr(req->env[i], '=')) != NULL) { + *s = '\0'; + name_len = s - req->env[i]; + } else { + name_len = strlen(req->env[i]); + } + for (j = 0; j < sizeof(fcgi_mgmt_vars)/sizeof(fcgi_mgmt_vars[0]); j++) { + if (strncmp(req->env[i], fcgi_mgmt_vars[j].name, name_len) == 0) { + sprintf((char*)p, "%c%c%s%c", name_len, 1, fcgi_mgmt_vars[j].name, fcgi_mgmt_vars[j].val); + p += name_len+3; + } + } + } + len = p - buf - sizeof(fcgi_header); + len += fcgi_make_header((fcgi_header*)buf, FCGI_GET_VALUES_RESULT, 0, len); + if (safe_write(req, buf, sizeof(fcgi_header)+len) != sizeof(fcgi_header)+len) { + return 0; + } + return 0; + } else { + return 0; + } + + return 1; +} + +int fcgi_read(fcgi_request *req, char *str, int len) +{ + int ret, n, rest; + fcgi_header hdr; + unsigned char buf[8]; + + n = 0; + rest = len; + while (rest > 0) { + if (req->in_len == 0) { + if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || + hdr.version < FCGI_VERSION_1 || + hdr.type != FCGI_STDIN) { + req->keep = 0; + return 0; + } + req->in_len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; + req->in_pad = hdr.paddingLength; + if (req->in_len == 0) { + return n; + } + } + + if (req->in_len >= rest) { + ret = safe_read(req, str, rest); + } else { + ret = safe_read(req, str, req->in_len); + } + if (ret < 0) { + req->keep = 0; + return ret; + } else if (ret > 0) { + req->in_len -= ret; + rest -= ret; + n += ret; + str += ret; + if (req->in_len == 0) { + if (req->in_pad) { + if (safe_read(req, buf, req->in_pad) != req->in_pad) { + req->keep = 0; + return ret; + } + } + } else { + return n; + } + } else { + return n; + } + } + return n; +} + +static inline void fcgi_close(fcgi_request *req, int force, int destroy) +{ + if (destroy) { + char **env = req->env; + while (*env) { + free(*env++); + } + } + if ((force || !req->keep) && req->fd >= 0) { +#ifdef _WIN32 + HANDLE pipe = (HANDLE)_get_osfhandle(req->fd); + + if (!force) { + FlushFileBuffers(pipe); + } + DisconnectNamedPipe(pipe); + if (is_impersonate) { + RevertToSelf(); + } +#else + close(req->fd); +#endif + req->fd = -1; + } +} + +int fcgi_accept_request(fcgi_request *req) +{ + fcgi_finish_request(req); + + while (1) { + if (req->fd < 0) { + while (1) { +#ifdef _WIN32 + HANDLE pipe = (HANDLE)_get_osfhandle(req->listen_socket); + OVERLAPPED ov; + + FCGI_LOCK(req->listen_socket); + ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!ConnectNamedPipe(pipe, &ov)) { + errno = GetLastError(); + if (errno == ERROR_IO_PENDING) { + while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) { + if (in_shutdown) { + CloseHandle(ov.hEvent); + FCGI_UNLOCK(req->listen_socket); + return -1; + } + } + } else if (errno != ERROR_PIPE_CONNECTED) { + } + } + CloseHandle(ov.hEvent); + if (is_impersonate && !ImpersonateNamedPipeClient(pipe)) { + DisconnectNamedPipe(pipe); + req->fd = -1; + } else { + req->fd = req->listen_socket; + } + FCGI_UNLOCK(req->listen_socket); +#else + sa_t sa; + socklen_t len = sizeof(sa); + + FCGI_LOCK(req->listen_socket); + req->fd = accept(req->listen_socket, (struct sockaddr *)&sa, &len); + FCGI_UNLOCK(req->listen_socket); +#endif + + if (in_shutdown || (req->fd < 0 && errno != EINTR)) { + return -1; + } + +#ifdef _WIN32 + break; +#else + if (req->fd >= 0) { + struct timeval tv = {1,0}; + fd_set set; + + FD_ZERO(&set); + FD_SET(req->fd, &set); +try_again: + errno = 0; + if (select(req->fd + 1, &set, NULL, NULL, &tv) >= 0 && FD_ISSET(req->fd, &set)) { + break; + } + if (errno == EINTR) goto try_again; + fcgi_close(req, 1, 0); + } +#endif + } + } + if (fcgi_read_request(req)) { + return req->fd; + } else { + fcgi_close(req, 1, 1); + } + } +} + +static inline fcgi_header* open_packet(fcgi_request *req, fcgi_request_type type) +{ + req->out_hdr = (fcgi_header*) req->out_pos; + req->out_hdr->type = type; + req->out_pos += sizeof(fcgi_header); + return req->out_hdr; +} + +static inline void close_packet(fcgi_request *req) +{ + if (req->out_hdr) { + int len = req->out_pos - ((unsigned char*)req->out_hdr + sizeof(fcgi_header)); + + req->out_pos += fcgi_make_header(req->out_hdr, req->out_hdr->type, req->id, len); + req->out_hdr = NULL; + } +} + +int fcgi_flush(fcgi_request *req, int close) +{ + int len; + + close_packet(req); + + len = req->out_pos - req->out_buf; + + if (close) { + fcgi_end_request_rec *rec = (fcgi_end_request_rec*)(req->out_pos); + + fcgi_make_header(&rec->hdr, FCGI_END_REQUEST, req->id, sizeof(fcgi_end_request)); + rec->body.appStatusB3 = 0; + rec->body.appStatusB2 = 0; + rec->body.appStatusB1 = 0; + rec->body.appStatusB0 = 0; + rec->body.protocolStatus = FCGI_REQUEST_COMPLETE; + len += sizeof(fcgi_end_request_rec); + } + + if (safe_write(req, req->out_buf, len) != len) { + req->keep = 0; + return 0; + } + + req->out_pos = req->out_buf; + return 1; +} + +int fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len) +{ + int limit, rest; + + if (len <= 0) { + return 0; + } + + if (req->out_hdr && req->out_hdr->type != type) { + close_packet(req); + } + rest = len; +#if 0 + /* Unoptinmzed, but clear version */ + while (rest > 0) { + limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf); + + if (!req->out_hdr) { + if (limit < sizeof(fcgi_header)) { + fcgi_flush(req, 0); + } + open_packet(req, type); + } + limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf); + if (rest < limit) { + memcpy(req->out_pos, str, rest); + req->out_pos += rest; + return len; + } else { + memcpy(req->out_pos, str, limit); + req->out_pos += limit; + rest -= limit; + str += limit; + fcgi_flush(req, 0); + } + } +#else + /* Optinmzed version */ + if (!req->out_hdr) { + rest += sizeof(fcgi_header); + } + limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf); + + if (rest < limit) { + if (!req->out_hdr) { + open_packet(req, type); + } + memcpy(req->out_pos, str, len); + req->out_pos += len; + } else if (rest - limit < sizeof(req->out_buf) - sizeof(fcgi_header)) { + if (!req->out_hdr) { + open_packet(req, type); + } + memcpy(req->out_pos, str, limit); + req->out_pos += limit; + fcgi_flush(req, 0); + open_packet(req, type); + memcpy(req->out_pos, str + limit, len - limit); + req->out_pos += len - limit; + } else { + int pad = ((len + 7) & ~7) - len; + + rest = pad ? 8 - pad : 0; + + close_packet(req); + open_packet(req, type); + fcgi_make_header(req->out_hdr, type, req->id, len - rest); + req->out_hdr = NULL; + fcgi_flush(req, 0); + if (safe_write(req, str, len - rest) != len - rest) { + req->keep = 0; + return -1; + } + if (pad) { + open_packet(req, type); + memcpy(req->out_pos, str + len - rest, rest); + req->out_pos += rest; + } + } +#endif + return len; +} + +int fcgi_finish_request(fcgi_request *req) +{ + fcgi_flush(req, 1); + fcgi_close(req, 0, 1); + return 1; +} + +char* fcgi_getenv_helper(char** env, const char *name, int len) +{ + if (name && env) { + while (*env) { + if ((strncmp(name, *env, len) == 0) && ((*env)[len] == '=')) { + return *env+len+1; + } + env++; + } + } + return NULL; +} + +char* fcgi_getenv(fcgi_request *req, const char* var, int var_len) +{ + if (!req) return NULL; + return fcgi_getenv_helper(req->env, var, var_len); +} + +void fcgi_putenv(fcgi_request *req, char* var, int var_len) +{ + if (var && req) { + char **env = req->env; + char *s = strchr(var, '='); + int len; + + if (!s) return ; + len = s - var + 1; + while (*env) { + if ((strncmp(var, *env, len) == 0)) { + free(*env); + *env = fcgi_strndup(var, var_len); + return; + } + env++; + } + *env = fcgi_strndup(var, var_len); + } +} + +int FCGX_FPrintF(FCGX_Stream stream, const char *format, ...) +{ + int result; + va_list args; + char buf[4096]; + + va_start(args, format); + result = vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + + fcgi_write(stream.req, stream.type, buf, result); + return result; +} + +#ifdef _WIN32 +void OS_SetImpersonate(void) +{ + char *os_name; + + os_name = getenv("OS"); + if (os_name && stricmp(os_name, "Windows_NT") == 0) { + is_impersonate = 1; + } +} +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ |