You are viewing a plain text version of this content. The canonical link for it is here.
Posted to apreq-dev@httpd.apache.org by Joe Schaefer <jo...@sunstarsys.com> on 2001/04/19 20:10:13 UTC

Perl hook interface, SFIO

Here's a patch that adds a Perl interface to David's hook code
(I had to drop the "const" declaration from the UploadHook prototype
in apache_request.h).  I've also patched apreq to work using PerlIO 
when perl-5.6.1/mod_perl-1.25 are compiled against SFIO.

To test SFIO support, build apreq with

  % perl MakeFile.PL DEFINE=-DSFIO

Earlier I announced to the modperl mailing list that I had a patch
which required the hook to write the upload data manually.  After
getting some comments and realizing that calling $upload->fh in the hook
would generate *lots* of symbols and file descriptors, I decided to
change this.  apreq will still handle writing the data to fh, and
$upload->fh still returns read-only filehandles.

It doesn't appear to add new leaks on linux, but I don't know what will
happen on other platforms.  Feedback is most appreciated.

Thanks.

-- 
Joe Schaefer

% cvs diff

cvs server: Diffing .
cvs server: Diffing Cookie
cvs server: Diffing Request
Index: Request/Request.pm
===================================================================
RCS file: /home/cvs/httpd-apreq/Request/Request.pm,v
retrieving revision 1.15
diff -u -r1.15 Request.pm
--- Request/Request.pm	2001/04/02 22:44:57	1.15
+++ Request/Request.pm	2001/04/19 17:49:39
@@ -97,6 +97,29 @@
  my $upload = $apr->upload('file');
  $upload->link("/home/user/myfile") || warn "link failed: $!";
 
+=item HOOK_DATA
+
+Extra configuration info to be passed to and upload hook.
+See next item for details.
+
+=item UPLOAD_HOOK
+
+Sets up a callback to run whenever file upload data is read. This
+can be used to provide an upload progress meter during file uploads.
+Apache will automatically continue writing the original data to
+$upload->fh after the hook exits.
+
+ my $transparent_hook = sub {
+   my ($upload, $buf, $len, $hook_data) = @_;
+   warn "$hook_data: got $len bytes for " . $upload->name;
+ };
+
+ my $apr = Apache::Request->new($r, 
+                                HOOK_DATA => "Note",
+                                UPLOAD_HOOK => $transparent_hook,
+                               );
+ $apr->parse;
+
 =back
 
 =head2 instance
Index: Request/Request.xs
===================================================================
RCS file: /home/cvs/httpd-apreq/Request/Request.xs,v
retrieving revision 1.13
diff -u -r1.13 Request.xs
--- Request/Request.xs	2001/04/03 19:21:37	1.13
+++ Request/Request.xs	2001/04/19 17:49:39
@@ -70,13 +70,17 @@
 
 typedef ApacheRequest * Apache__Request;
 typedef ApacheUpload  * Apache__Upload;
+typedef struct { 
+	SV* data;
+	SV* sub; 
+	} Hook;
 
 #define ApacheUpload_fh(upload)       upload->fp
 #define ApacheUpload_size(upload)     upload->size
 #define ApacheUpload_name(upload)     upload->name
 #define ApacheUpload_filename(upload) upload->filename
 #define ApacheUpload_next(upload)     upload->next
-#define ApacheUpload_tempname(upload)   upload->tempname
+#define ApacheUpload_tempname(upload) upload->tempname
 
 #ifndef PerlLIO_dup
 #define PerlLIO_dup(fd)   dup((fd)) 
@@ -90,6 +94,7 @@
 #else
 typedef FILE * InputStream;
 #define PerlIO_importFILE(fp,flags) fp
+#define PerlIO_write(a,b,c)  fwrite((b),1,(c),(a))
 #endif
 
 static char *r_keys[] = { "_r", "r", NULL };
@@ -140,6 +145,52 @@
     return sv; 
 } 
 
+static int upload_hook(void *ptr, char *buf, int len, ApacheUpload *upload)
+{
+    Hook *hook = (Hook*) ptr;
+
+    if ( upload->fp == NULL && ! ApacheRequest_tmpfile(upload->req, upload) )
+	    return -1; /* error */
+
+    {
+    	dTARG;
+    	dSP;
+
+    	PUSHMARK(SP);
+    	EXTEND(SP, 4);
+        ENTER;
+    	SAVETMPS;
+
+    	TARG = sv_newmortal();
+    	sv_setref_pv( TARG, "Apache::Upload", (void*)upload );
+    	PUSHTARG;
+
+    	TARG = sv_2mortal( newSVpvn(buf,len) );
+    	SvTAINT(TARG);
+    	PUSHTARG;
+
+    	TARG = sv_2mortal( newSViv(len) );
+    	SvTAINT(TARG);
+    	PUSHTARG;
+
+    	PUSHs(hook->data);
+
+    	PUTBACK;
+    	perl_call_sv(hook->sub, G_EVAL|G_DISCARD);
+    	FREETMPS;
+    	LEAVE;
+    }
+
+    return SvTRUE(ERRSV) ? -1 : PerlIO_write(upload->fp, buf, len);
+}
+
+static void clear_hook(void *ptr) {
+   Hook *hook = (Hook*) ptr;
+   hook->sub != Nullsv && sv_2mortal(hook->sub);
+   hook->data != Nullsv && sv_2mortal(hook->data);
+}
+
+
 #define upload_push(upload) \
     XPUSHs(sv_2mortal(upload_bless(upload))) 
 
@@ -156,6 +207,7 @@
 		    GvNAME(handle), GvNAMELEN(handle), G_DISCARD);
 }
 
+
 #ifdef CGI_COMPAT
 static void register_uploads (ApacheRequest *req) {
     ApacheUpload *upload;
@@ -189,7 +241,7 @@
     PREINIT:
     int i;
     SV *robj;
-
+	
     CODE:
     class = class; /* -Wall */ 
     robj = ST(1);
@@ -204,6 +256,17 @@
 		RETVAL->disable_uploads = (int)SvIV(ST(i+1));
 		break;
 	    }
+
+	case 'h':
+	   if (strcasecmp(key, "hook_data") == 0) {
+		if (RETVAL->hook_data == NULL) {
+		  RETVAL->hook_data = (void *)ap_pcalloc(r->pool, sizeof(Hook));
+		  ((Hook*)RETVAL->hook_data)->sub = Nullsv;
+ 		}
+		((Hook*)RETVAL->hook_data)->data = newSVsv(ST(i+1));	
+		break;
+	   }
+
 	case 'p':
 	    if (strcasecmp(key, "post_max") == 0) {
 		RETVAL->post_max = (int)SvIV(ST(i+1));
@@ -214,16 +277,31 @@
 		RETVAL->temp_dir = (char *)SvPV(ST(i+1), PL_na);
 		break;
 	    }
+	case 'u':
+	    if (strcasecmp(key, "upload_hook") == 0) {
+		if (RETVAL->hook_data == NULL) {
+		  RETVAL->hook_data = (void *)ap_pcalloc(r->pool, sizeof(Hook));
+	          ((Hook*)RETVAL->hook_data)->data = Nullsv;
+ 		}
+		((Hook*)RETVAL->hook_data)->sub = newSVsv(ST(i+1));
+		RETVAL->upload_hook = &upload_hook;
+		break;
+	    }
 	default:
 	    croak("[libapreq] unknown attribute: `%s'", key);
 	}
     }
 
+
     OUTPUT:
     RETVAL
 
     CLEANUP:
     apreq_add_magic(ST(0), robj, RETVAL);
+    if ( RETVAL->hook_data != NULL ) {
+    	ap_register_cleanup(r->pool, RETVAL->hook_data, 
+			   clear_hook, ap_null_cleanup);    
+    }
 
 char *
 ApacheRequest_script_name(req)
@@ -343,7 +421,8 @@
     ApacheUpload *uptr;
 
     PPCODE:
-    ApacheRequest_parse(req);
+    if ( !req->parsed ) ApacheRequest_parse(req);
+
     if (GIMME == G_SCALAR) {
 	if (name) {
 	    uptr = ApacheUpload_find(req->upload, name);
@@ -376,28 +455,29 @@
     Apache::Upload upload
 
     CODE:
-    if (!(RETVAL = PerlIO_importFILE(ApacheUpload_fh(upload),0))) {
-	XSRETURN_UNDEF;
-    }
+    if (  ( RETVAL = ApacheUpload_fh(upload) ) == NULL  )
+	    XSRETURN_UNDEF;
 
     OUTPUT:
     RETVAL
 
     CLEANUP:
-    if (ST(0) != &sv_undef) {
-	IO *io = GvIOn((GV*)SvRV(ST(0))); 
+    if (ST(0) != &PL_sv_undef) {
+	IO *io = GvIOn((GV*)SvRV(ST(0)));
 	int fd = PerlIO_fileno(IoIFP(io));
 	PerlIO *fp;
 
-	fd = PerlLIO_dup(fd); 
+	fd = PerlLIO_dup(fd);
 	if (!(fp = PerlIO_fdopen(fd, "r"))) { 
 	    PerlLIO_close(fd);
 	    croak("fdopen failed!");
-	} 
-	PerlIO_seek(fp, 0, 0); 
-	IoIFP(GvIOn((GV*)SvRV(ST(0)))) = fp;
+	}
+	if (upload->req->parsed)
+	    PerlIO_seek(fp, 0, 0);
+
+	IoIFP(GvIOn((GV*)SvRV(ST(0)))) = fp;  	
 	ap_register_cleanup(upload->req->r->pool, (void*)SvRV(ST(0)), 
-			    apreq_close_handle, ap_null_cleanup);    
+			    apreq_close_handle, ap_null_cleanup);      
     }
 
 long
cvs server: Diffing c
Index: c/apache_request.c
===================================================================
RCS file: /home/cvs/httpd-apreq/c/apache_request.c,v
retrieving revision 1.10
diff -u -r1.10 apache_request.c
--- c/apache_request.c	2001/03/15 15:05:35	1.10
+++ c/apache_request.c	2001/04/19 17:49:40
@@ -268,9 +268,8 @@
 int ApacheRequest___parse(ApacheRequest *req)
 {
     request_rec *r = req->r;
+    int result;
 
-    req->parsed = 1;
-
     if (r->args) {
         split_to_parms(req, r->args);
     }
@@ -278,20 +277,24 @@
     if (r->method_number == M_POST) { 
 	const char *ct = ap_table_get(r->headers_in, "Content-type"); 
 	if (ct && strcaseEQN(ct, DEFAULT_ENCTYPE, DEFAULT_ENCTYPE_LENGTH)) {
-	    return ApacheRequest_parse_urlencoded(req); 
+	    result = ApacheRequest_parse_urlencoded(req); 
 	}
 	else if (ct && strcaseEQN(ct, MULTIPART_ENCTYPE, MULTIPART_ENCTYPE_LENGTH)) {
-	   return ApacheRequest_parse_multipart(req); 
+	   result = ApacheRequest_parse_multipart(req); 
 	}
 	else {
 	    ap_log_rerror(REQ_ERROR, 
 			  "[libapreq] unknown content-type: `%s'", ct); 
-	    return HTTP_INTERNAL_SERVER_ERROR;
+	    result = HTTP_INTERNAL_SERVER_ERROR;
 	}
     } 
     else {
-	return ApacheRequest_parse_urlencoded(req); 
+	result = ApacheRequest_parse_urlencoded(req); 
     }
+
+    req->parsed = 1;
+    return result;
+
 }
 
 int ApacheRequest_parse_urlencoded(ApacheRequest *req)
@@ -319,8 +322,17 @@
 }
 
 static void remove_tmpfile(void *data) {
-    remove((char *) data);
-    free((char *) data);
+    ApacheUpload *upload = (ApacheUpload *) data;
+    ApacheRequest *req = upload->req;
+
+    if( ap_pfclose(req->r->pool, upload->fp) )
+	ap_log_rerror(REQ_ERROR,
+		      "[libapreq] close error on '%s'", upload->tempname);	
+    if( remove(upload->tempname) )
+	ap_log_rerror(REQ_ERROR,
+		      "[libapreq] remove error on '%s'", upload->tempname);
+
+    free(upload->tempname);
 }
 
 FILE *ApacheRequest_tmpfile(ApacheRequest *req, ApacheUpload *upload)
@@ -332,7 +344,8 @@
     int fd, tries = 100;
     
     while (--tries > 0) {
-	if ( (name = tempnam(req->temp_dir, prefix)) == NULL ) continue;
+	if ( (name = tempnam(req->temp_dir, prefix)) == NULL )
+	    continue;
 	fd = ap_popenf(r->pool, name, O_CREAT|O_EXCL|O_RDWR, 0600);
 	if ( fd >= 0 )
 	    break; /* success */
@@ -340,7 +353,7 @@
 	    free(name);
     }
     
-    if ( tries == 0  || (fp = ap_pfdopen(r->pool, fd, "w+") ) == NULL ) {
+    if ( tries == 0  || (fp = ap_pfdopen(r->pool, fd, "wb+") ) == NULL ) {
 	ap_log_rerror(REQ_ERROR,
 		      "[libapreq] could not open temp file '%s'", name); 	
 	if ( fd >= 0 ) { remove(name); free(name); }
@@ -349,7 +362,7 @@
 
     upload->fp = fp;
     upload->tempname = name;
-    ap_register_cleanup(r->pool, (void *)upload->tempname, 
+    ap_register_cleanup(r->pool, (void *)upload, 
 			remove_tmpfile, ap_null_cleanup);
     return fp;
 
@@ -462,11 +475,8 @@
 		upload->size += wlen;
 	    }
 
-	    if (upload->size > 0 && (req->upload_hook == NULL)) {
+	    if (upload->size > 0 && (upload->fp != NULL)) {
 		fseek(upload->fp, 0, 0);
-	    }
-	    else {
-		upload->fp = NULL;
 	    }
 	}
     }
Index: c/apache_request.h
===================================================================
RCS file: /home/cvs/httpd-apreq/c/apache_request.h,v
retrieving revision 1.5
diff -u -r1.5 apache_request.h
--- c/apache_request.h	2001/03/15 03:04:13	1.5
+++ c/apache_request.h	2001/04/19 17:49:40
@@ -10,6 +10,28 @@
 #include "http_protocol.h"
 #include "util_script.h"
 
+#ifdef  SFIO
+#include "sfio.h"
+
+/* sfio 2000 changed _stdopen to _stdfdopen */
+#if SFIO_VERSION >= 20000101L
+#define _stdopen _stdfdopen
+#endif
+
+extern Sfio_t*  _stdopen _ARG_((int, const char*)); /*1999*/
+
+#undef  FILE
+#define FILE 			Sfio_t
+#undef  fwrite
+#define fwrite(p,s,n,f)		sfwrite((f),(p),(s)*(n))
+#undef  fseek
+#define fseek(f,a,b)		sfseek((f),(a),(b))
+#undef  ap_pfdopen
+#define ap_pfdopen(p,q,r) 	_stdopen((q),(r))
+#undef  ap_pfclose
+#define ap_pfclose(p,q)		sfclose(q)
+#endif /*SFIO*/
+
 typedef struct ApacheUpload ApacheUpload;
 
 typedef struct {
@@ -19,7 +41,7 @@
     int parsed;
     int post_max;
     int disable_uploads;
-    int (*upload_hook)(void *ptr, char *buf, int len, const ApacheUpload *upload);
+    int (*upload_hook)(void *ptr, char *buf, int len, ApacheUpload *upload);
     void *hook_data;
     char* temp_dir;
     request_rec *r;
cvs server: Diffing eg
cvs server: Diffing eg/c
cvs server: Diffing eg/c/testapreq
cvs server: Diffing eg/perl
cvs server: Diffing lib
cvs server: Diffing lib/Apache