Ejemplo n.º 1
0
int main (void)
{
    int year, i;
    apr_time_t guess;
    apr_time_t offset = 0;
 /* apr_time_t offset = 0; */
 /* apr_time_t offset = ((31 + 28) * 24 * 3600) - 1; */
    apr_time_t secstodate, newsecs;
    char datestr[50];

    for (year = 1970; year < 2038; ++year) {
        secstodate = year2secs[year - 1970] + offset;
        gm_timestr_822(datestr, secstodate);
        secstodate *= APR_USEC_PER_SEC;
        newsecs = apr_date_parse_http(datestr);
        if (secstodate == newsecs)
            printf("Yes %4d %19" APR_TIME_T_FMT " %s\n", year, secstodate, datestr);
        else if (newsecs == APR_DATE_BAD)
            printf("No  %4d %19" APR_TIME_T_FMT " %19" APR_TIME_T_FMT " %s\n",
                   year, secstodate, newsecs, datestr);
        else
            printf("No* %4d %19" APR_TIME_T_FMT " %19" APR_TIME_T_FMT " %s\n",
                   year, secstodate, newsecs, datestr);
    }
    
    srand48(978245L);

    for (i = 0; i < 10000; ++i) {
        guess = (time_t)mrand48();
        if (guess < 0) guess *= -1;
        secstodate = guess + offset;
        gm_timestr_822(datestr, secstodate);
        secstodate *= APR_USEC_PER_SEC;
        newsecs = apr_date_parse_http(datestr);
        if (secstodate == newsecs)
            printf("Yes %" APR_TIME_T_FMT " %s\n", secstodate, datestr);
        else if (newsecs == APR_DATE_BAD)
            printf("No  %" APR_TIME_T_FMT " %" APR_TIME_T_FMT " %s\n", 
                   secstodate, newsecs, datestr);
        else
            printf("No* %" APR_TIME_T_FMT " %" APR_TIME_T_FMT " %s\n", 
                   secstodate, newsecs, datestr);
    }
    exit(0);
}
Ejemplo n.º 2
0
static void fcgi_write_response(mapcache_context_fcgi *ctx, mapcache_http_response *response)
{
  if(response->code != 200) {
    printf("Status: %ld %s\r\n",response->code, err_msg(response->code));
  }
  if(response->headers && !apr_is_empty_table(response->headers)) {
    const apr_array_header_t *elts = apr_table_elts(response->headers);
    int i;
    for(i=0; i<elts->nelts; i++) {
      apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t);
      printf("%s: %s\r\n", entry.key, entry.val);
    }
  }
  if(response->mtime) {
    char *datestr;
    char *if_modified_since = getenv("HTTP_IF_MODIFIED_SINCE");

    datestr = apr_palloc(ctx->ctx.pool, APR_RFC822_DATE_LEN);
    apr_rfc822_date(datestr, response->mtime);
    printf("Last-Modified: %s\r\n", datestr);

    if(if_modified_since) {
      apr_time_t ims_time;
      apr_int64_t ims,mtime;


      mtime =  apr_time_sec(response->mtime);
      ims_time = apr_date_parse_http(if_modified_since);
      ims = apr_time_sec(ims_time);
      if(ims >= mtime) {
        printf("Status: 304 Not Modified\r\n");
	/*
	 * "The 304 response MUST NOT contain a message-body"
	 * https://tools.ietf.org/html/rfc2616#section-10.3.5
	 */
	printf("\r\n");
	return;
      }
    }
  }
  if(response->data) {
    printf("Content-Length: %ld\r\n\r\n", response->data->size);
    fwrite((char*)response->data->buf, response->data->size,1,stdout);
  }
}
Ejemplo n.º 3
0
static void accept_headers(cache_handle_t *h, request_rec *r)
{
    apr_table_t *cookie_table;
    const char *v;

    v = apr_table_get(h->resp_hdrs, "Content-Type");
    if (v) {
        ap_set_content_type(r, v);
        apr_table_unset(h->resp_hdrs, "Content-Type");
    }

    /* If the cache gave us a Last-Modified header, we can't just
     * pass it on blindly because of restrictions on future values.
     */
    v = apr_table_get(h->resp_hdrs, "Last-Modified");
    if (v) {
        ap_update_mtime(r, apr_date_parse_http(v));
        ap_set_last_modified(r);
        apr_table_unset(h->resp_hdrs, "Last-Modified");
    }

    /* The HTTP specification says that it is legal to merge duplicate
     * headers into one.  Some browsers that support Cookies don't like
     * merged headers and prefer that each Set-Cookie header is sent
     * separately.  Lets humour those browsers by not merging.
     * Oh what a pain it is.
     */
    cookie_table = apr_table_make(r->pool, 2);
    apr_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out,
                 "Set-Cookie", NULL);
    apr_table_do(set_cookie_doo_doo, cookie_table, h->resp_hdrs,
                 "Set-Cookie", NULL);
    apr_table_unset(r->err_headers_out, "Set-Cookie");
    apr_table_unset(h->resp_hdrs, "Set-Cookie");

    apr_table_overlap(r->headers_out, h->resp_hdrs,
                      APR_OVERLAP_TABLES_SET);
    apr_table_overlap(r->err_headers_out, h->resp_err_hdrs,
                      APR_OVERLAP_TABLES_SET);
    if (!apr_is_empty_table(cookie_table)) {
        r->err_headers_out = apr_table_overlay(r->pool, r->err_headers_out,
                                               cookie_table);
    }
}
/**
 * Take two sets of headers, sandwich them together, and apply the result to
 * r->headers_out.
 *
 * To complicate this, a header may be duplicated in either table. Should a
 * header exist in the top table, all matching headers will be removed from
 * the bottom table before the headers are combined. The Warning headers are
 * handled specially. Warnings are added rather than being replaced, while
 * in the case of revalidation 1xx Warnings are stripped.
 *
 * The Content-Type and Last-Modified headers are then re-parsed and inserted
 * into the request.
 */
void cache_accept_headers(cache_handle_t *h, request_rec *r, apr_table_t *top,
        apr_table_t *bottom, int revalidation)
{
    const char *v;

    if (revalidation) {
        r->headers_out = apr_table_make(r->pool, 10);
        apr_table_do(filter_header_do, r->headers_out, bottom, NULL);
    }
    else if (r->headers_out != bottom) {
        r->headers_out = apr_table_copy(r->pool, bottom);
    }
    apr_table_do(remove_header_do, r->headers_out, top, NULL);
    apr_table_do(add_header_do, r->headers_out, top, NULL);

    v = apr_table_get(r->headers_out, "Content-Type");
    if (v) {
        ap_set_content_type(r, v);
        /*
         * Also unset possible Content-Type headers in r->headers_out and
         * r->err_headers_out as they may be different to what we have received
         * from the cache.
         * Actually they are not needed as r->content_type set by
         * ap_set_content_type above will be used in the store_headers functions
         * of the storage providers as a fallback and the HTTP_HEADER filter
         * does overwrite the Content-Type header with r->content_type anyway.
         */
        apr_table_unset(r->headers_out, "Content-Type");
        apr_table_unset(r->err_headers_out, "Content-Type");
    }

    /* If the cache gave us a Last-Modified header, we can't just
     * pass it on blindly because of restrictions on future values.
     */
    v = apr_table_get(r->headers_out, "Last-Modified");
    if (v) {
        ap_update_mtime(r, apr_date_parse_http(v));
        ap_set_last_modified(r);
    }

}
Ejemplo n.º 5
0
AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
                                       int (*getsfunc) (char *, int, void *),
                                       void *getsfunc_data)
{
    char x[MAX_STRING_LEN];
    char *w, *l;
    int p;
    int cgi_status = HTTP_UNSET;
    apr_table_t *merge;
    apr_table_t *cookie_table;

    if (buffer) {
        *buffer = '\0';
    }
    w = buffer ? buffer : x;

    /* temporary place to hold headers to merge in later */
    merge = apr_table_make(r->pool, 10);

    /* The HTTP specification says that it is legal to merge duplicate
     * headers into one.  Some browsers that support Cookies don't like
     * merged headers and prefer that each Set-Cookie header is sent
     * separately.  Lets humour those browsers by not merging.
     * Oh what a pain it is.
     */
    cookie_table = apr_table_make(r->pool, 2);
    apr_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out, "Set-Cookie", NULL);

    while (1) {

        int rv = (*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data);
        if (rv == 0) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
                          "Premature end of script headers: %s",
                          apr_filepath_name_get(r->filename));
            return HTTP_INTERNAL_SERVER_ERROR;
        }
        else if (rv == -1) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
                          "Script timed out before returning headers: %s",
                          apr_filepath_name_get(r->filename));
            return HTTP_GATEWAY_TIME_OUT;
        }

        /* Delete terminal (CR?)LF */

        p = strlen(w);
             /* Indeed, the host's '\n':
                '\012' for UNIX; '\015' for MacOS; '\025' for OS/390
                 -- whatever the script generates.
             */
        if (p > 0 && w[p - 1] == '\n') {
            if (p > 1 && w[p - 2] == CR) {
                w[p - 2] = '\0';
            }
            else {
                w[p - 1] = '\0';
            }
        }

        /*
         * If we've finished reading the headers, check to make sure any
         * HTTP/1.1 conditions are met.  If so, we're done; normal processing
         * will handle the script's output.  If not, just return the error.
         * The appropriate thing to do would be to send the script process a
         * SIGPIPE to let it know we're ignoring it, close the channel to the
         * script process, and *then* return the failed-to-meet-condition
         * error.  Otherwise we'd be waiting for the script to finish
         * blithering before telling the client the output was no good.
         * However, we don't have the information to do that, so we have to
         * leave it to an upper layer.
         */
        if (w[0] == '\0') {
            int cond_status = OK;

           /* PR#38070: This fails because it gets confused when a
            * CGI Status header overrides ap_meets_conditions.
            * 
            * We can fix that by dropping ap_meets_conditions when
            * Status has been set.  Since this is the only place
            * cgi_status gets used, let's test it explicitly.
            *
            * The alternative would be to ignore CGI Status when
            * ap_meets_conditions returns anything interesting.
            * That would be safer wrt HTTP, but would break CGI.
            */
            if ((cgi_status == HTTP_UNSET) && (r->method_number == M_GET)) {
                cond_status = ap_meets_conditions(r);
            }
            apr_table_overlap(r->err_headers_out, merge,
                APR_OVERLAP_TABLES_MERGE);
            if (!apr_is_empty_table(cookie_table)) {
                /* the cookies have already been copied to the cookie_table */
                apr_table_unset(r->err_headers_out, "Set-Cookie");
                r->err_headers_out = apr_table_overlay(r->pool,
                    r->err_headers_out, cookie_table);
            }
            return cond_status;
        }

        /* if we see a bogus header don't ignore it. Shout and scream */

#if APR_CHARSET_EBCDIC
            /* Chances are that we received an ASCII header text instead of
             * the expected EBCDIC header lines. Try to auto-detect:
             */
        if (!(l = strchr(w, ':'))) {
            int maybeASCII = 0, maybeEBCDIC = 0;
            unsigned char *cp, native;
            apr_size_t inbytes_left, outbytes_left;

            for (cp = w; *cp != '\0'; ++cp) {
                native = apr_xlate_conv_byte(ap_hdrs_from_ascii, *cp);
                if (apr_isprint(*cp) && !apr_isprint(native))
                    ++maybeEBCDIC;
                if (!apr_isprint(*cp) && apr_isprint(native))
                    ++maybeASCII;
            }
            if (maybeASCII > maybeEBCDIC) {
                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
                             "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)",
                             r->filename);
                inbytes_left = outbytes_left = cp - w;
                apr_xlate_conv_buffer(ap_hdrs_from_ascii,
                                      w, &inbytes_left, w, &outbytes_left);
            }
        }
#endif /*APR_CHARSET_EBCDIC*/
        if (!(l = strchr(w, ':'))) {
            char malformed[(sizeof MALFORMED_MESSAGE) + 1
                           + MALFORMED_HEADER_LENGTH_TO_SHOW];

            strcpy(malformed, MALFORMED_MESSAGE);
            strncat(malformed, w, MALFORMED_HEADER_LENGTH_TO_SHOW);

            if (!buffer) {
                /* Soak up all the script output - may save an outright kill */
                while ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data)) {
                    continue;
                }
            }

            ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
                          "%s: %s", malformed,
                          apr_filepath_name_get(r->filename));
            return HTTP_INTERNAL_SERVER_ERROR;
        }

        *l++ = '\0';
        while (*l && apr_isspace(*l)) {
            ++l;
        }

        if (!strcasecmp(w, "Content-type")) {
            char *tmp;

            /* Nuke trailing whitespace */

            char *endp = l + strlen(l) - 1;
            while (endp > l && apr_isspace(*endp)) {
                *endp-- = '\0';
            }

            tmp = apr_pstrdup(r->pool, l);
            ap_content_type_tolower(tmp);
            ap_set_content_type(r, tmp);
        }
        /*
         * If the script returned a specific status, that's what
         * we'll use - otherwise we assume 200 OK.
         */
        else if (!strcasecmp(w, "Status")) {
            r->status = cgi_status = atoi(l);
            r->status_line = apr_pstrdup(r->pool, l);
        }
        else if (!strcasecmp(w, "Location")) {
            apr_table_set(r->headers_out, w, l);
        }
        else if (!strcasecmp(w, "Content-Length")) {
            apr_table_set(r->headers_out, w, l);
        }
        else if (!strcasecmp(w, "Content-Range")) {
            apr_table_set(r->headers_out, w, l);
        }
        else if (!strcasecmp(w, "Transfer-Encoding")) {
            apr_table_set(r->headers_out, w, l);
        }
        /*
         * If the script gave us a Last-Modified header, we can't just
         * pass it on blindly because of restrictions on future values.
         */
        else if (!strcasecmp(w, "Last-Modified")) {
            ap_update_mtime(r, apr_date_parse_http(l));
            ap_set_last_modified(r);
        }
        else if (!strcasecmp(w, "Set-Cookie")) {
            apr_table_add(cookie_table, w, l);
        }
        else {
            apr_table_add(merge, w, l);
        }
    }

    return OK;
}
Ejemplo n.º 6
0
static int
lisp_handler (request_rec * r)
{
  lisp_cfg_t * cfg
    = (ap_get_module_config ((r->per_dir_config), (&lisp_module)));
  int content_length = (-1);
  int keep_socket_p = 0;
  apr_socket_t * socket;
  const char * request_content_length = 0;

  cfg = local_lisp_cfg(cfg);

  if ((strcmp ((r->handler), "lisp-handler")) != 0)
    return (DECLINED);

  /* Open a connection to the Lisp process.  */
  ML_LOG_DEBUG (r, "open lisp connection");
  CVT_ERROR ((open_lisp_socket (cfg)), "opening connection to Lisp");
  (SERVER_SOCKET_SAFE_P (cfg)) = 0;
  socket = (SERVER_SOCKET (cfg));

  /* Remove any timeout that might be left over from earlier.  */
  ML_LOG_DEBUG (r, "clear socket timeout");
  CVT_ERROR ((apr_socket_timeout_set (socket, (-1))), "clearing read timeout");
  
  /* Convert environment variables to headers and send them.  */
  ML_LOG_DEBUG (r, "write env-var headers");
  ap_add_cgi_vars (r);
  ap_add_common_vars (r);
  if ((r->subprocess_env) != 0)
    CVT_ERROR
      ((copy_headers
	 	((r->subprocess_env), map_env_var_to_lisp_header, socket)),
              "writing to Lisp");

  /* Send this before client headers so ASSOC can be used to grab it
     without worrying about some joker sending a server-id header of
     his own.  (Robert Macomber) */
  ML_LOG_DEBUG (r, "write headers");
  CVT_ERROR ((write_lisp_header (socket, "server-id", (cfg->server_id))),
	     	     "writing to Lisp");

  CVT_ERROR ((write_lisp_header (socket, "server-baseversion", AP_SERVER_BASEVERSION)),
	     	     "writing to Lisp");
  CVT_ERROR ((write_lisp_header (socket, "modlisp-version", VERSION_STRING)),
	     	     "writing to Lisp");
  CVT_ERROR ((write_lisp_header (socket, "modlisp-major-version", "2")),
	     	     "writing to Lisp");
  /* Send all the remaining headers.  */
  if ((r->headers_in) != 0)
    CVT_ERROR
      ((copy_headers ((r->headers_in), map_header_to_lisp_header, socket)),
              "writing to Lisp");

  request_content_length = apr_table_get(r->headers_in, "Content-Length");

  /* Send the end-of-headers marker.  */
  ML_LOG_DEBUG (r, "write end-of-headers");
  CVT_ERROR ((write_lisp_line (socket, "end")), "writing to Lisp");

  /* Send the request entity.  */
  RELAY_HTTP_ERROR (ap_setup_client_block (r, REQUEST_CHUNKED_DECHUNK));
  if (ap_should_client_block (r))
    {
      char buffer [4096];
      ML_LOG_DEBUG (r, "write entity");
      while (1)
	{
	  long n_read = (ap_get_client_block (r, buffer, (sizeof (buffer))));
	  if (n_read < 0)
	    {
	      ML_LOG_PERROR (r, "error reading from client");
	      close_lisp_socket (cfg);
	      return (HTTP_INTERNAL_SERVER_ERROR);
	    }

	  /* for chunked case, when nread == 0, we will write 
	   * a terminating 0.*/
	  
	  {
	    apr_status_t status = APR_SUCCESS; 
	    
	    /* if there's no Content-Type header, the data must be chunked */
	    if (request_content_length == NULL)
	      status = write_lisp_data_chunk (socket, buffer, n_read);
	    else if (n_read != 0)
	      status = write_lisp_data (socket, buffer, n_read);
	    
	    if (APR_SUCCESS != status)
	      {
		while ((ap_get_client_block (r, buffer, sizeof(buffer)))
		       > 0)
		  ;
		ML_LOG_ERROR (status, r, "writing to Lisp");
		close_lisp_socket (cfg);
		return (HTTP_INTERNAL_SERVER_ERROR);
	      }
	  }
	  if( n_read == 0)
	    break;
	}
    }

  /* Set up read timeout so we don't hang forever if Lisp is wedged.  */
  ML_LOG_DEBUG (r, "set socket timeout");
  CVT_ERROR ((apr_socket_timeout_set (socket, READ_TIMEOUT)),
	     	     "setting read timeout");

  /* Read the headers and process them.  */
  ML_LOG_DEBUG (r, "read headers");
  while (1)
    {
      char header_name [4096];
      char header_value [MAX_STRING_LEN];
      CVT_ERROR
	((read_lisp_line (socket, header_name, (sizeof (header_name)))),
	 	 "reading from Lisp");

      if ((strcasecmp (header_name, "end")) == 0)
	break;

      CVT_ERROR
	((read_lisp_line (socket, header_value, (sizeof (header_value)))),
	 	 "reading from Lisp");

      if ((strcasecmp (header_name, "content-type")) == 0)
	{
	  char * tmp = (apr_pstrdup ((r->pool), header_value));
	  ap_content_type_tolower (tmp);
	  (r->content_type) = tmp;
	}
      else if ((strcasecmp (header_name, "status")) == 0)
	{
	  (r->status) = (atoi (header_value));
	  (r->status_line) = (apr_pstrdup ((r->pool), header_value));
	}
      else if ((strcasecmp (header_name, "location")) == 0)
	apr_table_set ((r->headers_out), header_name, header_value);
      else if ((strcasecmp (header_name, "content-length")) == 0)
	{
	  apr_table_set ((r->headers_out), header_name, header_value);
	  content_length = (atoi (header_value));
	}
      else if ((strcasecmp (header_name, "lisp-content-length")) == 0)
	{
	  content_length = (atoi (header_value));
	}
      else if ((strcasecmp (header_name, "last-modified")) == 0)
	{
	  apr_time_t mtime = (apr_date_parse_http (header_value));
	  r->mtime = mtime;
	  ap_set_last_modified (r);
	}
      else if ((strcasecmp (header_name, "keep-socket")) == 0)
	keep_socket_p = (atoi (header_value));
      else if ((strcasecmp (header_name, "log-emerg")) == 0)
	ap_log_error (APLOG_MARK, APLOG_EMERG, APR_SUCCESS, (r->server),
				  		      "%s", header_value);
      else if ((strcasecmp (header_name, "log-alert")) == 0)
	ap_log_error (APLOG_MARK, APLOG_ALERT, APR_SUCCESS, (r->server),
				  		      "%s", header_value);
      else if ((strcasecmp (header_name, "log-crit")) == 0)
	ap_log_error (APLOG_MARK, APLOG_CRIT, APR_SUCCESS, (r->server),
				  		      "%s", header_value);
      else if ((strcasecmp (header_name, "log-error")) == 0)
	ap_log_error (APLOG_MARK, APLOG_ERR, APR_SUCCESS, (r->server),
				  		      "%s", header_value);
      else if ((strcasecmp (header_name, "log-warning")) == 0)
	ap_log_error (APLOG_MARK, APLOG_WARNING, APR_SUCCESS, (r->server),
				  		      "%s", header_value);
      else if ((strcasecmp (header_name, "log-notice")) == 0)
	ap_log_error (APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, (r->server),
				  		      "%s", header_value);
      else if ((strcasecmp (header_name, "log-info")) == 0)
	ap_log_error (APLOG_MARK, APLOG_INFO, APR_SUCCESS, (r->server),
				  		      "%s", header_value);
      else if ((strcasecmp (header_name, "log-debug")) == 0)
	ap_log_error (APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, (r->server),
				  		      "%s", header_value);
      else if ((strcasecmp (header_name, "log")) == 0)
	ap_log_error (APLOG_MARK, APLOG_ERR, APR_SUCCESS, (r->server),
				  		      "%s", header_value);
      else if ((strcasecmp (header_name, "note")) == 0)
	{
	  char * p = (strchr (header_value, ' '));
	  if (p != 0)
	    {
	      (*p++) = '\0';
	      apr_table_setn ((r->notes),
			      			      (apr_pstrdup ((r->pool), header_value)),
						      			      (apr_pstrdup ((r->pool), p)));
	    }
	}
      else if ((strcasecmp (header_name, "set-cookie")) == 0)
	{
	  apr_table_add ((r->headers_out), header_name, header_value);
	}
      else
	apr_table_set ((r->headers_out), header_name, header_value);
    }

  /* Copy the reply entity from Lisp to the client...  */
  //  if (content_length > 0)
  {
    unsigned int n_read = 0;
    input_buffer_t * buffer;

    ML_LOG_DEBUG (r, "read entity");
    CVT_ERROR ((get_input_buffer (socket, (&buffer))), "reading from Lisp");
    while ((buffer->start) <= (buffer->end))
      {
	apr_status_t fill_status;
        unsigned int n_bytes = ((buffer->end) - (buffer->start));
        n_read += n_bytes;
        if ((content_length >= 0) && (n_read > content_length))
          {
            n_bytes -= (n_read - content_length);
            n_read -= (n_read - content_length);
          }
        /* ...unless it's a HEAD request. */
        if (!r->header_only && !write_client_data (r, (buffer->start), n_bytes))
          {
            close_lisp_socket (cfg);
            return (HTTP_INTERNAL_SERVER_ERROR);
          }
        (buffer->start) += n_bytes;
        if (n_read == content_length)
          break;

        fill_status = fill_input_buffer (socket);
        if ((fill_status == APR_EOF) && (content_length < 0))
          break;
        else
          CVT_ERROR (fill_status, "reading from Lisp");
      }
  }
  if ((content_length < 0) || (!keep_socket_p))
    CVT_ERROR ((close_lisp_socket (cfg)), "closing connection to Lisp");
  else
    (SERVER_SOCKET_SAFE_P (cfg)) = 1;
  ML_LOG_DEBUG (r, "request finished");
  return (OK);
}
Ejemplo n.º 7
0
apr_status_t jxr_process_response_headers(request_rec *r, char *buf)
{
	apr_status_t rv = APR_SUCCESS;
	
	apr_size_t pos;
	apr_size_t len;
	char type;
	int nHeaders;
	int i;
	char name[MAX_STRING_LEN];
	char value[MAX_STRING_LEN];
	apr_size_t nlen, vlen;
	table *merge;
	table *cookie_table;
	char *w, *l;
	
	type = jxr_msg_get_type(buf);
	len = jxr_msg_get_length(buf,  &pos);

	if (type != BLOCKTYPE_HTTP_HEADER)
	{
		// Invalid data
		compat_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, "mod_jaxer: invalid data type (%c) received, while expecting a header (%d)", type, BLOCKTYPE_HTTP_HEADER);
		return HTTP_INTERNAL_SERVER_ERROR;
	}

	/* temporary place to hold headers to merge in later */
    merge = ap_make_table(r->pool, 10);

	cookie_table = ap_make_table(r->pool, 2);
    ap_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out, "Set-Cookie", NULL);

	nHeaders = jxr_msg_get_int16(buf, &pos);
	for (i=0; i<nHeaders; i++)
	{
		// Process one header -- name -- val
		nlen = jxr_msg_get_string(buf, &pos, name);
		vlen = jxr_msg_get_string(buf, &pos, value);

		w = name;
		l = value;

		if (!strcasecmp(w, "Content-type")) {
            char *tmp;

            /* Nuke trailing whitespace */

            char *endp = l + strlen(l) - 1;
            while (endp > l && apr_isspace(*endp)) {
                *endp-- = '\0';
            }

            tmp = ap_pstrdup(r->pool, l);
            ap_content_type_tolower(tmp);
            ap_set_content_type(r, tmp);
        }
        else if (!strcasecmp(w, "Status")) {
		/*
         * If the server returned a specific status, that's what
         * we'll use - otherwise we assume 200 OK.
         */
        
            r->status = atoi(l);
            r->status_line = ap_pstrdup(r->pool, l);
        }
        else if (!strcasecmp(w, "Location")) {
            ap_table_set(r->headers_out, w, l);
        }
        else if (!strcasecmp(w, "Content-Length")) {
            ap_table_set(r->headers_out, w, l);
        }
        else if (!strcasecmp(w, "Content-Range")) {
            ap_table_set(r->headers_out, w, l);
        }
        else if (!strcasecmp(w, "Transfer-Encoding")) {
            ap_table_set(r->headers_out, w, l);
        }
        else if (!strcasecmp(w, "Last-Modified")) {
        /*
         * If the script gave us a Last-Modified header, we can't just
         * pass it on blindly because of restrictions on future values.
         */
            ap_update_mtime(r, apr_date_parse_http(l));
            ap_set_last_modified(r);
        }
        else if (!strcasecmp(w, "Set-Cookie")) {
            ap_table_add(cookie_table, w, l);
        }
        else {
            ap_table_add(merge, w, l);
        }
	}


	// now merge stuff
	ap_overlap_tables(r->err_headers_out, merge,
        AP_OVERLAP_TABLES_MERGE);
    if (!ap_is_empty_table(cookie_table)) {
        /* the cookies have already been copied to the cookie_table */
        ap_table_unset(r->err_headers_out, "Set-Cookie");
        r->err_headers_out = ap_overlay_tables(r->pool,
            r->err_headers_out, cookie_table);
    }

	return rv;
}