Bug 8882 - [PATCH] mod_rewrite communicates with external rewrite engine over TCP/IP
Summary: [PATCH] mod_rewrite communicates with external rewrite engine over TCP/IP
Status: RESOLVED WONTFIX
Alias: None
Product: Apache httpd-1.3
Classification: Unclassified
Component: mod_rewrite (show other bugs)
Version: 1.3.24
Hardware: All All
: P3 enhancement (vote)
Target Milestone: ---
Assignee: Apache HTTPD Bugs Mailing List
URL:
Keywords: PatchAvailable
Depends on:
Blocks:
 
Reported: 2002-05-07 16:26 UTC by James Tait
Modified: 2007-08-02 11:11 UTC (History)
0 users



Attachments
Patch to allow mod_rewrite to talk to an external rewrite engine over TCP/IP (5.70 KB, patch)
2002-05-08 14:35 UTC, James Tait
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description James Tait 2002-05-07 16:26:40 UTC
The stock mod_rewrite interface with an external rewrite engine via stdin/stdout
requires a lockfile to prevent multiple child processes screwing up each other's
rewriting.  This essentially means that all rewrite processes are serialised,
and can result in a serious queuing of requests if the rewrite program
encounters any delay.

The following patch will enable mod_rewrite to communicate with the external
rewrite engine over TCP/IP sockets, meaning that a multi-threaded (or
multi-process, you choose) rewrite engine can deal with several rewrite requests
at once, without the need to serialise the requests.  I'm not suggesting for one
minute that it's perfect, or even close, but it's a means to an end and it
actually works.

There's a slight change to the RewriteMap directive:

RewriteMap name "prg:/path/to/prog args" port

Where port is the port to send rewrite requests to.  I'm sure this could be done
more elegantly by having an extra RewriteMap type, but tying it into the prg:
type does have the advantage of ensuring that the process is up and running at
the same time Apache is.

--- src/modules/standard/mod_rewrite.c  Tue May  7 16:34:43 2002
+++ /home/jtait/src/apache_1.3.24/src/modules/standard/mod_rewrite.c    Tue May
 7 16:14:34 2002
@@ -168,7 +168,7 @@
       "an input string and a to be applied regexp-pattern" },
     { "RewriteRule",     cmd_rewriterule,     NULL, OR_FILEINFO, RAW_ARGS,
       "an URL-applied regexp-pattern and a substitution URL" },
-    { "RewriteMap",      cmd_rewritemap,      NULL, RSRC_CONF,   TAKE2,
+    { "RewriteMap",      cmd_rewritemap,      NULL, RSRC_CONF,   TAKE23,
       "a mapname and a filename" },
     { "RewriteLock",     cmd_rewritelock,     NULL, RSRC_CONF,   TAKE1,
       "the filename of a lockfile used for inter-process synchronization"},
@@ -448,7 +448,7 @@
 }

 static const char *cmd_rewritemap(cmd_parms *cmd, void *dconf, char *a1,
-                                  char *a2)
+                                  char *a2, char *a3)
 {
     rewrite_server_conf *sconf;
     rewritemap_entry *new;
@@ -485,6 +485,12 @@
         new->type = MAPTYPE_PRG;
         new->datafile = a2+4;
         new->checkfile = a2+4;
+        if (a3 == NULL) {
+            new->port = 0;
+        }
+       else {
+            new->port = atoi(a3);
+        }
     }
     else if (strncmp(a2, "int:", 4) == 0) {
         new->type      = MAPTYPE_INT;
@@ -2753,7 +2759,7 @@
             }
             else if (s->type == MAPTYPE_PRG) {
                 if ((value =
-                     lookup_map_program(r, s->fpin, s->fpout, key)) != NULL) {
+                     lookup_map_program(r, s->fpin, s->fpout, key, s->port)) !=
NULL) {
                     rewritelog(r, 5, "map lookup OK: map=%s key=%s -> val=%s",
                                s->name, key, value);
                     return value;
@@ -2891,11 +2897,12 @@
 }
 #endif

-static char *lookup_map_program(request_rec *r, int fpin, int fpout, char *key)
+static char *lookup_map_program(request_rec *r, int fpin, int fpout, char *key,
unsigned short port)
 {
     char buf[LONG_STRING_LEN];
     char c;
     int i;
+    int sock;
 #ifndef NO_WRITEV
     struct iovec iov[2];
 #endif
@@ -2909,33 +2916,71 @@
         return NULL;
     }

-    /* take the lock */
-    rewritelock_alloc(r);
+    if (port != 0) {
+        /* create a socket connection to the external program */
+        sock = make_socket_connection(r, port);
+        if (sock == -1) {
+            return NULL;
+        }
+
+        ap_hard_timeout("Send external rewrite request", r);
+        /* write out the request key */
+        i = send(sock, key, strlen(key), 0);
+        if (i != strlen(key)) {
+            ap_kill_timeout(r);
+            return NULL;
+        }
+        ap_reset_timeout(r);
+
+        i = send(sock, "\n", 1, 0);
+        if (i != 1) {
+            ap_kill_timeout(r);
+            return NULL;
+        }
+        ap_kill_timeout(r);
+
+        /* read in the response value */
+        ap_hard_timeout("Receive external rewrite response", r);
+        i = 0;
+        while (recv(sock, &c, 1, 0) == 1 && (i < LONG_STRING_LEN -1)) {
+            if (c == '\n') {
+                break;
+            }
+            buf[i++] = c;
+        }
+        buf[i] = '\0';
+        ap_pclosesocket(r->pool, sock);
+        ap_kill_timeout(r);
+    }
+    else {
+        /* take the lock */
+        rewritelock_alloc(r);

-    /* write out the request key */
+        /* write out the request key */
 #ifdef NO_WRITEV
-    write(fpin, key, strlen(key));
-    write(fpin, "\n", 1);
+        write(fpin, key, strlen(key));
+        write(fpin, "\n", 1);
 #else
-    iov[0].iov_base = key;
-    iov[0].iov_len = strlen(key);
-    iov[1].iov_base = "\n";
-    iov[1].iov_len = 1;
-    writev(fpin, iov, 2);
+        iov[0].iov_base = key;
+        iov[0].iov_len = strlen(key);
+        iov[1].iov_base = "\n";
+        iov[1].iov_len = 1;
+        writev(fpin, iov, 2);
 #endif

-    /* read in the response value */
-    i = 0;
-    while (read(fpout, &c, 1) == 1 && (i < LONG_STRING_LEN-1)) {
-        if (c == '\n') {
-            break;
+        /* read in the response value */
+        i=0;
+        while (read(fpout, &c, 1) == 1 && (i < LONG_STRING_LEN-1)) {
+            if (c == '\n') {
+                break;
+            }
+            buf[i++] = c;
         }
-        buf[i++] = c;
-    }
-    buf[i] = '\0';
+        buf[i] = '\0';

-    /* give the lock back */
-    rewritelock_free(r);
+        /* give the lock back */
+        rewritelock_free(r);
+    }

     if (strcasecmp(buf, "NULL") == 0) {
         return NULL;
@@ -2945,6 +2990,45 @@
     }
 }

+static int make_socket_connection(request_rec *r, unsigned short port)
+{
+    struct hostent *server_hp;
+    struct sockaddr_in addr;
+    int sock;
+    int i;
+
+    memset(&addr, '\0', sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port   = htons(port);
+
+    server_hp = gethostbyname("localhost");
+    addr.sin_addr.s_addr =  ((struct in_addr *)server_hp->h_addr_list[0])->s_addr;
+
+    sock = ap_psocket(r->pool, PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (sock == -1) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "Unable to create socket");
+        return -1;
+    }
+
+    ap_hard_timeout("External rewriter connect", r);
+    do {
+        i = connect(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));
+#if defined(WIN32) || defined(NETWARE)
+        if (i == SOCKET_ERROR) {
+            errno = WSAGetLastError();
+        }
+#endif /* WIN32 */
+    } while (i == -1 && errno == EINTR);
+    if (i == -1) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                      "Unable to connect to external rewriter on %s:%d",
+                      inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+        sock = -1;
+    }
+    ap_kill_timeout(r);
+    return sock;
+}
+
 static char *lookup_map_internal(request_rec *r,
                                  char *(*func)(request_rec *, char *),
                                  char *key)
--- src/modules/standard/mod_rewrite.h  Tue May  7 16:34:43 2002
+++ /home/jtait/src/apache_1.3.24/src/modules/standard/mod_rewrite.h    Fri May
 3 17:18:39 2002
@@ -274,6 +274,7 @@
     int   fperr;                   /* err file pointer for program maps */
     char *(*func)(request_rec *,   /* function pointer for internal maps */
                   char *);
+    unsigned short port;           /* port number for program maps */
 } rewritemap_entry;

 typedef struct {
@@ -383,7 +384,7 @@
 static const char *cmd_rewritelog     (cmd_parms *cmd, void *dconf, char *a1);
 static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf, char *a1);
 static const char *cmd_rewritemap     (cmd_parms *cmd, void *dconf, char *a1,
-                                       char *a2);
+                                       char *a2, char *a3);
 static const char *cmd_rewritelock(cmd_parms *cmd, void *dconf, char *a1);
 static const char *cmd_rewritebase(cmd_parms *cmd, rewrite_perdir_conf *dconf,
                                    char *a1);
@@ -440,7 +441,8 @@
 static char *lookup_map_dbmfile(request_rec *r, char *file, char *key);
 #endif
 static char *lookup_map_program(request_rec *r, int fpin,
-                                int fpout, char *key);
+                                int fpout, char *key, unsigned short port);
+static int make_socket_connection(request_rec *r, unsigned short port);
 static char *lookup_map_internal(request_rec *r,
                                  char *(*func)(request_rec *r, char *key),
                                  char *key);
Comment 1 James Tait 2002-05-08 14:28:49 UTC
OK, I've spent some time coming up with a better solution.  The following patch
is much more elegant, and allows Apache to talk to another server altogether
(what TCP/IP was designed for, right?).  It doesn't ensure that the external
rewrite engine is started, but it's a small price to pay for a potentially large
increase in performance.

The new format is:

RewriteMap tcp:ipaddr.or.dnsname.of.host:port

The port does not have a default value, so it must be specified.

--- src/modules/standard/mod_rewrite.c  Sat Mar 16 23:44:20 2002
+++ /home/jtait/src/apache_1.3.24/src/modules/standard/mod_rewrite.c    Wed May
 8 15:18:36 2002
@@ -507,6 +507,11 @@
                               a2+4, NULL);
         }
     }
+    else if (strncmp(a2, "tcp:", 4) == 0) {
+        new->type      = MAPTYPE_TCP;
+        new->datafile  = a2+4;
+        new->checkfile = NULL;
+    }
     else {
         new->type      = MAPTYPE_TXT;
         new->datafile  = a2;
@@ -2751,6 +2756,17 @@
                 return NULL;
 #endif
             }
+            else if (s->type == MAPTYPE_TCP) {
+                if ((value = lookup_map_tcp(r, s->datafile, key)) != NULL) {
+                    rewritelog(r, 5, "map lookup OK: map=%s(%s) key=%s -> val=%s",
+                               s->name, s->datafile, key, value);
+                    return value;
+                }
+                else {
+                    rewritelog(r, 5, "map lookup FAILED: map=%s(%s), key=%s",
+                               s->name, s->datafile, key);
+                }
+            }
             else if (s->type == MAPTYPE_PRG) {
                 if ((value =
                      lookup_map_program(r, s->fpin, s->fpout, key)) != NULL) {
@@ -2945,6 +2961,126 @@
     }
 }

+static char *lookup_map_tcp(request_rec *r, const char *hoststr, char *key)
+{
+    char buf[LONG_STRING_LEN];
+    char c;
+    int i, sock;
+
+    /* create a socket connection to the external program */
+    sock = make_socket_connection(r, hoststr);
+    if (sock == -1) {
+        return NULL;
+    }
+
+    ap_hard_timeout("Send external rewrite request", r);
+    /* write out the request key */
+    i = send(sock, key, strlen(key), 0);
+    if (i != strlen(key)) {
+        ap_kill_timeout(r);
+        return NULL;
+    }
+    ap_reset_timeout(r);
+
+    i = send(sock, "\n", 1, 0);
+    if (i != 1) {
+        ap_kill_timeout(r);
+        return NULL;
+    }
+    ap_kill_timeout(r);
+
+    /* read in the response value */
+    ap_hard_timeout("Receive external rewrite response", r);
+    i = 0;
+    while (recv(sock, &c, 1, 0) == 1 && (i < LONG_STRING_LEN - 1)) {
+        if (c == '\n') {
+            break;
+        }
+        buf[i++] = c;
+    }
+    buf[i] = '\0';
+    ap_pclosesocket(r->pool, sock);
+    ap_kill_timeout(r);
+
+    if (strcasecmp(buf, "NULL") == 0) {
+        return NULL;
+    }
+    else {
+        return ap_pstrdup(r->pool, buf);
+    }
+}
+
+static int make_socket_connection(request_rec *r, const char *hoststr)
+{
+    struct hostent *server_hp;
+    struct sockaddr_in addr;
+    int i, sock, port;
+    char *host;
+    char *portstr;
+
+    memset(&addr, '\0', sizeof(addr));
+    addr.sin_family = AF_INET;
+
+    host = ap_pstrdup(r->pool, hoststr);
+    rewritelog(r, 5, "Extracting port from string: [%s]", host);
+
+    port = 0;
+    portstr = strchr(host, ':');
+    if (portstr != NULL) {
+        *(portstr++) = '\0';
+        rewritelog(r, 5, "Port found: [%s]", portstr);
+        if (ap_isdigit(*portstr)) {
+            port = atoi(portstr);
+        }
+    }
+
+    addr.sin_port = htons(port);
+
+    for (i = 0; host[i] != '\0'; i++) {
+        if(!ap_isdigit(host[i]) && host[i] != '.') {
+            break;
+        }
+    }
+    if (host[i] == '\0') {
+        rewritelog(r, 3, "External rewrite host IP: %s:%d", host, port);
+        addr.sin_addr.s_addr = inet_addr(host);
+    }
+    else {
+        rewritelog(r, 3, "External rewrite hostname: %s:%d", host, port);
+        server_hp = gethostbyname(host);
+        if (host == NULL) {
+            addr.sin_addr.s_addr = 0;
+        }
+        else {
+            addr.sin_addr.s_addr = ((struct in_addr
*)server_hp->h_addr_list[0])->s_addr;
+        }
+    }
+
+    sock = ap_psocket(r->pool, PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (sock == -1) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "Unable to create socket");
+        return -1;
+    }
+
+    ap_hard_timeout("External rewriter connect", r);
+    do {
+        i = connect(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in));
+#if defined(WIN32) || defined(NETWARE)
+        if (i == SOCKET_ERROR) {
+            errno = WSAGetLastError();
+        }
+#endif /* WIN32 */
+    } while (i == -1 && errno == EINTR);
+    if (i == -1) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                      "Unable to connect to external rewriter on %s:%d",
+                      inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+        sock = -1;
+    }
+    ap_kill_timeout(r);
+    return sock;
+}
+
 static char *lookup_map_internal(request_rec *r,
                                  char *(*func)(request_rec *, char *),
                                  char *key)
--- src/modules/standard/mod_rewrite.h  Wed Mar 13 21:05:34 2002
+++ /home/jtait/src/apache_1.3.24/src/modules/standard/mod_rewrite.h    Wed May
 8 15:00:22 2002
@@ -220,6 +220,7 @@
 #define MAPTYPE_PRG                 1<<2
 #define MAPTYPE_INT                 1<<3
 #define MAPTYPE_RND                 1<<4
+#define MAPTYPE_TCP                 1<<5

 #define ENGINE_DISABLED             1<<0
 #define ENGINE_ENABLED              1<<1
@@ -441,6 +442,8 @@
 #endif
 static char *lookup_map_program(request_rec *r, int fpin,
                                 int fpout, char *key);
+static char *lookup_map_tcp(request_rec *r, const char *hoststr, char *key);
+static int make_socket_connection(request_rec *r, const char *hoststr);
 static char *lookup_map_internal(request_rec *r,
                                  char *(*func)(request_rec *r, char *key),
                                  char *key);
Comment 2 James Tait 2002-05-08 14:35:28 UTC
Created attachment 1812 [details]
Patch to allow mod_rewrite to talk to an external rewrite engine over TCP/IP
Comment 3 Jeff Trawick 2003-11-21 18:05:51 UTC
I'm going through the bug db to make sure patches are findable.  Please see 
http://httpd.apache.org/dev/patches.html