You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl-cvs@perl.apache.org by do...@apache.org on 2001/01/02 07:41:19 UTC

cvs commit: modperl-2.0/src/modules/perl modperl_filter.c modperl_filter.h

dougm       01/01/01 22:41:19

  Added:       src/modules/perl modperl_filter.c modperl_filter.h
  Log:
  filter routines
  
  Revision  Changes    Path
  1.1                  modperl-2.0/src/modules/perl/modperl_filter.c
  
  Index: modperl_filter.c
  ===================================================================
  #include "mod_perl.h"
  
  /* simple buffer api */
  
  MP_INLINE apr_status_t modperl_wbucket_pass(modperl_wbucket_t *wb,
                                              const char *buf, apr_ssize_t len)
  {
      ap_bucket_brigade *bb = ap_brigade_create(wb->pool);
      ap_bucket *bucket = ap_bucket_create_transient(buf, len);
      AP_BRIGADE_INSERT_TAIL(bb, bucket);
      return ap_pass_brigade(wb->filters, bb);
  }
  
  MP_INLINE apr_status_t modperl_wbucket_flush(modperl_wbucket_t *wb)
  {
      apr_status_t rv = APR_SUCCESS;
  
      if (wb->outcnt) {
          rv = modperl_wbucket_pass(wb, wb->outbuf, wb->outcnt);
          wb->outcnt = 0;
      }
  
      return rv;
  }
  
  MP_INLINE apr_status_t modperl_wbucket_write(modperl_wbucket_t *wb,
                                               const char *buf,
                                               apr_ssize_t *wlen)
  {
      apr_ssize_t len = *wlen;
      *wlen = 0;
  
      if ((len + wb->outcnt) > sizeof(wb->outbuf)) {
          apr_status_t rv;
          if ((rv = modperl_wbucket_flush(wb)) != APR_SUCCESS) {
              return rv;
          }
      }
  
      if (len >= sizeof(wb->outbuf)) {
          *wlen = len;
          return modperl_wbucket_pass(wb, buf, len);
      }
      else {
          memcpy(&wb->outbuf[wb->outcnt], buf, len);
          wb->outcnt += len;
          *wlen = len;
          return APR_SUCCESS;
      }
  }
  
  /* generic filter routines */
  
  static char *filter_classes[] = {
      "Apache::InputFilter",
      "Apache::OutputFilter",
  };
  
  modperl_filter_t *modperl_filter_new(ap_filter_t *f,
                                       ap_bucket_brigade *bb,
                                       modperl_filter_mode_e mode)
  {
      apr_pool_t *p = mode == MP_INPUT_FILTER_MODE ?
          f->c->pool : f->r->pool;
      modperl_filter_t *filter = apr_pcalloc(p, sizeof(*filter));
  
      filter->mode = mode;
      filter->f = f;
      filter->bb = bb;
      filter->pool = p;
      filter->wbucket.pool = p;
      filter->wbucket.filters = f->next;
      filter->wbucket.outcnt = 0;
  
      MP_TRACE_f(MP_FUNC, "filter=0x%lx, mode=%s\n",
                 (unsigned long)filter, mode == MP_OUTPUT_FILTER_MODE ?
                 "output" : "input");
  
      return filter;
  }
  
  int modperl_run_filter(modperl_filter_t *filter)
  {
      int status;
      modperl_handler_t *handler =
          ((modperl_filter_ctx_t *)filter->f->ctx)->handler;
  
      request_rec *r = filter->f->r;
      conn_rec    *c = filter->f->c;
      server_rec  *s = r ? r->server : NULL;
      apr_pool_t  *p = r ? r->pool : c->pool;
  
  #ifdef USE_ITHREADS
      pTHX;
      modperl_interp_t *interp = NULL;
      interp = modperl_interp_select(r, c, s);
      handler->perl = aTHX = interp->perl;
      PERL_SET_CONTEXT(aTHX);
  #endif
  
      handler->args = newAV();
  
      modperl_handler_make_args(aTHX_ handler->args,
                                filter_classes[filter->mode], filter,
                                NULL);
  
      if ((status = modperl_callback(aTHX_ handler, p)) != OK) {
          status = modperl_errsv(aTHX_ status, r, s);
      }
  
      SvREFCNT_dec((SV*)handler->args);
      handler->args = Nullav;
  
      MP_TRACE_f(MP_FUNC, "%s returned %d\n", handler->name, status);
  
  
      return status;
  }
  
  MP_INLINE modperl_filter_t *modperl_sv2filter(pTHX_ SV *sv)
  {
      modperl_filter_t *filter = NULL;
  
      if (SvROK(sv) && (SvTYPE(SvRV(sv)) == SVt_PVMG)) {
          filter = (modperl_filter_t *)SvIV((SV*)SvRV(sv));
      }
  
      return filter;
  }
  
  /* output filters */
  
  MP_INLINE static apr_status_t send_eos(ap_filter_t *f)
  {
      ap_bucket_brigade *bb = ap_brigade_create(f->r->pool);
      ap_bucket *b = ap_bucket_create_eos();
      AP_BRIGADE_INSERT_TAIL(bb, b);
      return ap_pass_brigade(f->next, bb);
  }
  
  /* unrolled AP_BRIGADE_FOREACH loop */
  
  #define MP_FILTER_SENTINEL(filter) \
  AP_RING_SENTINEL(&(filter->bb)->list, ap_bucket, link)
  
  #define MP_FILTER_FIRST(filter) \
  AP_RING_FIRST(&(filter->bb)->list)
  
  #define MP_FILTER_NEXT(filter) \
  AP_RING_NEXT(filter->bucket, link)
  
  #define MP_FILTER_IS_EOS(filter) \
  (filter->bucket && AP_BUCKET_IS_EOS(filter->bucket))
  
  MP_INLINE static int get_bucket(modperl_filter_t *filter)
  {
      if (!filter->bb) {
          return 0;
      }
      if (!filter->bucket) {
          filter->bucket = MP_FILTER_FIRST(filter);
          return 1;
      }
      else if (MP_FILTER_IS_EOS(filter)) {
          filter->eos = 1;
          return 1;
      }
      else if (filter->bucket != MP_FILTER_SENTINEL(filter)) {
          filter->bucket = MP_FILTER_NEXT(filter);
          if (filter->bucket == MP_FILTER_SENTINEL(filter)) {
              ap_brigade_destroy(filter->bb);
              filter->bb = NULL;
              return 0;
          }
          else {
              return 1;
          }
      }
  
      return 0;
  }
  
  MP_INLINE apr_ssize_t modperl_output_filter_read(pTHX_
                                                   modperl_filter_t *filter,
                                                   SV *buffer,
                                                   apr_ssize_t wanted)
  {
      int num_buckets = 0;
      apr_ssize_t len = 0;
  
      (void)SvUPGRADE(buffer, SVt_PV);
      SvPOK_only(buffer);
      SvCUR(buffer) = 0;
  
      /*modperl_brigade_dump(filter->bb);*/
  
      MP_TRACE_f(MP_FUNC, "caller wants %d bytes\n", wanted);
  
      if (filter->remaining) {
          if (filter->remaining >= wanted) {
              MP_TRACE_f(MP_FUNC, "eating %d of remaining %d leftover bytes\n",
                         wanted, filter->remaining);
              sv_catpvn(buffer, filter->leftover, wanted);
              filter->leftover += wanted;
              filter->remaining -= wanted;
              return wanted;
          }
          else {
              MP_TRACE_f(MP_FUNC, "eating remaining %d leftover bytes\n",
                         filter->remaining);
              sv_catpvn(buffer, filter->leftover, filter->remaining);
              len = filter->remaining;
              filter->remaining = 0;
              filter->leftover = NULL;
          }
      }
  
      if (!filter->bb) {
          MP_TRACE_f(MP_FUNC, "bucket brigade has been emptied\n");
          return 0;
      }
  
      while (1) {
          const char *buf;
          apr_ssize_t buf_len;
  
          if (!get_bucket(filter)) {
              break;
          }
  
          if (MP_FILTER_IS_EOS(filter)) {
              MP_TRACE_f(MP_FUNC, "received EOS bucket\n");
              filter->eos = 1;
              break;
          }
  
          num_buckets++;
  
          filter->rc = ap_bucket_read(filter->bucket, &buf, &buf_len, 0);
  
          if (filter->rc == APR_SUCCESS) {
              MP_TRACE_f(MP_FUNC,
                         "bucket=%s(0x%lx) read returned %d bytes\n",
                         filter->bucket->type->name,
                         (unsigned long)filter->bucket,
                         buf_len);
          }
          else {
              MP_TRACE_f(MP_FUNC,
                         "ap_bucket_read error: %s\n",
                         modperl_apr_strerror(filter->rc));
              return len;
          }
  
          if (buf_len) {
              if ((SvCUR(buffer) + buf_len) >= wanted) {
                  int nibble = wanted - SvCUR(buffer);
                  sv_catpvn(buffer, buf, nibble);
                  filter->leftover = (char *)buf+nibble;
                  filter->remaining = buf_len - nibble;
                  len += nibble;
                  break;
              }
              else {
                  len += buf_len;
                  sv_catpvn(buffer, buf, buf_len);
              }
          }
      }
  
  #ifdef MP_TRACE
      if (num_buckets) {
          MP_TRACE_f(MP_FUNC,
                     "returning %d bytes from %d bucket%s "
                     "(%d bytes leftover)\n",
                     len, num_buckets, ((num_buckets > 1) ? "s" : ""),
                     filter->remaining);
      }
  #endif
  
      if (filter->eos && (len == 0)) {
          /* if len > 0 then $filter->write will flush */
          modperl_output_filter_flush(filter);
      }
  
      return len;
  }
  
  MP_INLINE apr_status_t modperl_output_filter_flush(modperl_filter_t *filter)
  {
      filter->rc = modperl_wbucket_flush(&filter->wbucket);
      if (filter->rc != APR_SUCCESS) {
          return filter->rc;
      }
  
      if (filter->eos) {
          MP_TRACE_f(MP_FUNC, "sending EOS bucket\n");
          filter->rc = send_eos(filter->f);
          ap_brigade_destroy(filter->bb);
          filter->bb = NULL;
          filter->eos = 0;
      }
  
      return filter->rc;
  }
  
  MP_INLINE apr_status_t modperl_output_filter_write(modperl_filter_t *filter,
                                                     const char *buf,
                                                     apr_ssize_t *len)
  {
      return modperl_wbucket_write(&filter->wbucket, buf, len);
  }
  
  #define AP_BRIGADE_IS_EOS(bb) \
  AP_BUCKET_IS_EOS(AP_RING_FIRST(&(bb)->list))
  
  apr_status_t modperl_output_filter_handler(ap_filter_t *f,
                                             ap_bucket_brigade *bb)
  {
      modperl_filter_t *filter;
      int status;
  
      if (AP_BRIGADE_IS_EOS(bb)) {
          /* XXX: see about preventing this in the first place */
          MP_TRACE_f(MP_FUNC, "first bucket is EOS, skipping callback\n");
          return ap_pass_brigade(f->next, bb);
      }
      else {
          filter = modperl_filter_new(f, bb, MP_OUTPUT_FILTER_MODE);
          status = modperl_run_filter(filter);
      }
  
      switch (status) {
        case OK:
          return APR_SUCCESS;
        case DECLINED:
          return ap_pass_brigade(f->next, bb);
        default:
          return status; /*XXX*/
      }
  }
  
  void modperl_output_filter_register(request_rec *r)
  {
      MP_dDCFG;
      MpAV *av;
  
      if ((av = dcfg->handlers[MP_OUTPUT_FILTER_HANDLER])) {
          modperl_handler_t **handlers = (modperl_handler_t **)av->elts;
          int i;
  
          for (i=0; i<av->nelts; i++) {
              modperl_filter_ctx_t *ctx =
                  (modperl_filter_ctx_t *)apr_pcalloc(r->pool, sizeof(*ctx));
              ctx->handler = handlers[i];
              ap_add_output_filter(MODPERL_OUTPUT_FILTER_NAME,
                                   (void*)ctx, r, r->connection);
          }
      }
  }
  
  void modperl_brigade_dump(ap_bucket_brigade *bb, FILE *fp)
  {
      ap_bucket *bucket;
      int i = 0;
  
      if (fp == NULL) {
          fp = stderr;
      }
  
      fprintf(fp, "dump of brigade 0x%lx\n",
              (unsigned long)bb);
  
      AP_BRIGADE_FOREACH(bucket, bb) {
          fprintf(fp, "   %d: bucket=%s(0x%lx), length=%ld, data=0x%lx\n",
                  i, bucket->type->name,
                  (unsigned long)bucket,
                  (long)bucket->length,
                  (unsigned long)bucket->data);
          i++;
      }
  }
  
  
  
  1.1                  modperl-2.0/src/modules/perl/modperl_filter.h
  
  Index: modperl_filter.h
  ===================================================================
  #ifndef MODPERL_FILTER_H
  #define MODPERL_FILTER_H
  
  #define MODPERL_OUTPUT_FILTER_NAME "MODPERL_OUTPUT"
  
  /* simple buffer api */
  MP_INLINE apr_status_t modperl_wbucket_pass(modperl_wbucket_t *b,
                                              const char *buf, apr_ssize_t len);
  
  MP_INLINE apr_status_t modperl_wbucket_flush(modperl_wbucket_t *b);
  
  MP_INLINE apr_status_t modperl_wbucket_write(modperl_wbucket_t *b,
                                               const char *buf,
                                               apr_ssize_t *wlen);
  
  /* generic filter routines */
  
  modperl_filter_t *modperl_filter_new(ap_filter_t *f,
                                       ap_bucket_brigade *bb,
                                       modperl_filter_mode_e mode);
  
  int modperl_run_filter(modperl_filter_t *filter);
  
  MP_INLINE modperl_filter_t *modperl_sv2filter(pTHX_ SV *sv);
  
  /* output filters */
  apr_status_t modperl_output_filter_handler(ap_filter_t *f,
                                             ap_bucket_brigade *bb);
  
  void modperl_output_filter_register(request_rec *r);
  
  MP_INLINE apr_status_t modperl_output_filter_flush(modperl_filter_t *filter);
  
  MP_INLINE apr_ssize_t modperl_output_filter_read(pTHX_
                                                   modperl_filter_t *filter,
                                                   SV *buffer,
                                                   apr_ssize_t wanted);
  
  MP_INLINE apr_status_t modperl_output_filter_write(modperl_filter_t *filter,
                                                     const char *buf,
                                                     apr_ssize_t *len);
  
  void modperl_brigade_dump(ap_bucket_brigade *bb, FILE *fp);
  
  #endif /* MODPERL_FILTER_H */