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