/* to assist in debugging mod_dav's GET handling */
#define DEBUG_GET_HANDLER 0
-#define DEBUG_PATHNAME_STYLE 0
#define DAV_FS_COPY_BLOCKSIZE 16384 /* copy 16k at a time */
apr_int32_t flags;
switch (mode) {
- case DAV_MODE_READ:
- case DAV_MODE_READ_SEEKABLE:
default:
flags = APR_READ | APR_BINARY;
break;
return NULL;
}
-static dav_error * dav_fs_read_stream(dav_stream *stream,
- void *buf, apr_size_t *bufsize)
-{
- if (apr_file_read(stream->f, buf, bufsize) != APR_SUCCESS) {
- /* ### use something besides 500? */
- return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
- "An error occurred while reading from a "
- "resource.");
- }
- return NULL;
-}
-
static dav_error * dav_fs_write_stream(dav_stream *stream,
const void *buf, apr_size_t bufsize)
{
return NULL;
}
+
+#if DEBUG_GET_HANDLER
+
+/* only define set_headers() and deliver() for debug purposes */
+
+
static dav_error * dav_fs_set_headers(request_rec *r,
const dav_resource *resource)
{
/* ### this function isn't really used since we have a get_pathname */
-#if DEBUG_GET_HANDLER
if (!resource->exists)
return NULL;
/* ### how to set the content type? */
/* ### until this is resolved, the Content-Type header is busted */
-#endif
-
return NULL;
}
-#if DEBUG_PATHNAME_STYLE
-static const char * dav_fs_get_pathname(
- const dav_resource *resource,
- void **free_handle_p)
+static dav_error * dav_fs_deliver(const dav_resource *resource,
+ ap_filter_t *output)
{
- return resource->info->pathname;
-}
-#endif
+ apr_pool_t *pool = resource->pool;
+ apr_bucket_brigade *bb;
+ apr_file_t *fd;
+ apr_status_t status;
+ apr_bucket *bkt;
-static void dav_fs_free_file(void *free_handle)
-{
- /* nothing to free ... */
+ /* Check resource type */
+ if (resource->type != DAV_RESOURCE_TYPE_REGULAR
+ && resource->type != DAV_RESOURCE_TYPE_VERSION
+ && resource->type != DAV_RESOURCE_TYPE_WORKING) {
+ return dav_new_error(pool, HTTP_CONFLICT, 0,
+ "Cannot GET this type of resource.");
+ }
+ if (resource->collection) {
+ return dav_new_error(pool, HTTP_CONFLICT, 0,
+ "There is no default response to GET for a "
+ "collection.");
+ }
+
+ if ((status = apr_file_open(&fd, resource->info->pathname,
+ APR_READ | APR_BINARY, 0,
+ pool)) != APR_SUCCESS) {
+ return dav_new_error(pool, HTTP_FORBIDDEN, 0,
+ "File permissions deny server access.");
+ }
+
+ bb = apr_brigade_create(pool);
+
+ /* ### this does not handle large files. but this is test code anyway */
+ bkt = apr_bucket_file_create(fd, 0,
+ (apr_size_t)resource->info->finfo.size,
+ pool);
+ APR_BRIGADE_INSERT_TAIL(bb, bkt);
+
+ bkt = apr_bucket_eos_create();
+ APR_BRIGADE_INSERT_TAIL(bb, bkt);
+
+ if ((status = ap_pass_brigade(output, bb)) != APR_SUCCESS) {
+ return dav_new_error(pool, HTTP_FORBIDDEN, 0,
+ "Could not write contents to filter.");
+ }
+
+ return NULL;
}
+#endif /* DEBUG_GET_HANDLER */
+
+
static dav_error * dav_fs_create_collection(dav_resource *resource)
{
dav_resource_private *ctx = resource->info;
dav_fs_is_parent_resource,
dav_fs_open_stream,
dav_fs_close_stream,
- dav_fs_read_stream,
dav_fs_write_stream,
dav_fs_seek_stream,
+#if DEBUG_GET_HANDLER
dav_fs_set_headers,
-#if DEBUG_PATHNAME_STYLE
- dav_fs_get_pathname,
+ dav_fs_deliver,
#else
- 0,
+ NULL,
+ NULL,
#endif
- dav_fs_free_file,
dav_fs_create_collection,
dav_fs_copy_resource,
dav_fs_move_resource,
static int dav_method_get(request_rec *r)
{
dav_resource *resource;
- int result;
dav_error *err;
/* This method should only be called when the resource is not
return HTTP_NOT_FOUND;
}
- /* Check resource type */
- if (resource->type != DAV_RESOURCE_TYPE_REGULAR &&
- resource->type != DAV_RESOURCE_TYPE_VERSION &&
- resource->type != DAV_RESOURCE_TYPE_WORKING)
- {
- return dav_error_response(r, HTTP_CONFLICT,
- "Cannot GET this type of resource.");
- }
-
- /* Cannot handle GET of a collection from a repository */
- if (resource->collection) {
- return dav_error_response(r, HTTP_CONFLICT,
- "No default response to GET for a "
- "collection.");
+ /* set up the HTTP headers for the response */
+ if ((err = (*resource->hooks->set_headers)(r, resource)) != NULL) {
+ err = dav_push_error(r->pool, err->status, 0,
+ "Unable to set up HTTP headers.",
+ err);
+ return dav_handle_err(r, err, NULL);
}
- /*
- ** We can use two different approaches for a GET.
- **
- ** 1) get_pathname will return a pathname to a file which should be
- ** sent to the client. If the repository provides this, then we
- ** use it.
- **
- ** This is the best alternative since it allows us to do a sub-
- ** request on the file, which gives the Apache framework a chance
- ** to deal with negotiation, MIME types, or whatever.
- **
- ** 2) open_stream and read_stream.
- */
- if (resource->hooks->get_pathname != NULL) {
- const char *pathname;
- void *fhandle;
- request_rec *new_req;
-
- /* Ask repository for copy of file */
- pathname = (*resource->hooks->get_pathname)(resource, &fhandle);
- if (pathname == NULL) {
- return HTTP_NOT_FOUND;
- }
-
- /* Create a sub-request with the new filename
- * The new_req filename is canonicalized by ap_sub_req_lookup_file()
- */
- new_req = ap_sub_req_lookup_file(pathname, r, NULL);
- if (new_req == NULL) {
- (*resource->hooks->free_file)(fhandle);
- return HTTP_INTERNAL_SERVER_ERROR;
- }
-
- /* This may be a HEAD request */
- new_req->header_only = r->header_only;
-
- /* ### this enables header generation */
- new_req->assbackwards = 0;
-
- /* Run the sub-request */
- result = ap_run_sub_req(new_req);
- ap_destroy_sub_req(new_req);
-
- /* Free resources */
- (*resource->hooks->free_file)(fhandle);
-
- return result;
+ if (r->header_only) {
+ return DONE;
}
- else {
- dav_stream *stream;
- void *buffer;
- /* set up the HTTP headers for the response */
- if ((err = (*resource->hooks->set_headers)(r, resource)) != NULL) {
- err = dav_push_error(r->pool, err->status, 0,
- "Unable to set up HTTP headers.",
- err);
- return dav_handle_err(r, err, NULL);
- }
-
- if (r->header_only) {
- return DONE;
- }
-
- if ((err = (*resource->hooks->open_stream)(resource, DAV_MODE_READ,
- &stream)) != NULL) {
- /* ### assuming FORBIDDEN is probably not quite right... */
- err = dav_push_error(r->pool, HTTP_FORBIDDEN, 0,
- apr_psprintf(r->pool,
- "Unable to GET contents for %s.",
- ap_escape_html(r->pool, r->uri)),
- err);
- return dav_handle_err(r, err, NULL);
- }
-
- buffer = apr_palloc(r->pool, DAV_READ_BLOCKSIZE);
- while (1) {
- apr_size_t amt = DAV_READ_BLOCKSIZE;
-
- if ((err = (*resource->hooks->read_stream)(stream, buffer,
- &amt)) != NULL) {
- break;
- }
- if (amt == 0) {
- /* no more content */
- break;
- }
- if (ap_rwrite(buffer, amt, r) < 0) {
- /* ### what to do with this error? */
- break;
- }
- }
-
- if (err != NULL)
- return dav_handle_err(r, err, NULL);
-
- return DONE;
+ /* okay... time to deliver the content */
+ if ((err = (*resource->hooks->deliver)(resource,
+ r->output_filters)) != NULL) {
+ err = dav_push_error(r->pool, err->status, 0,
+ "Unable to deliver content.",
+ err);
+ return dav_handle_err(r, err, NULL);
}
- /* NOTREACHED */
+ return DONE;
}
/* validate resource on POST, then pass it off to the default handler */
**
** Note that the structure is opaque -- it is private to the repository
** that created the stream in the repository's "open" function.
+**
+** ### THIS STUFF IS GOING AWAY ... GET/read requests are handled by
+** ### having the provider jam stuff straight into the filter stack.
+** ### this is only left for handling PUT/write requests.
*/
typedef struct dav_stream dav_stream;
typedef enum {
- DAV_MODE_READ, /* open for reading */
- DAV_MODE_READ_SEEKABLE, /* open for random access reading */
DAV_MODE_WRITE_TRUNC, /* truncate and open for writing */
DAV_MODE_WRITE_SEEKABLE /* open for writing; random access */
} dav_stream_mode;
*/
dav_error * (*close_stream)(dav_stream *stream, int commit);
- /*
- ** Read data from the stream.
- **
- ** The size of the buffer is passed in *bufsize, and the amount read
- ** is returned in *bufsize.
- **
- ** *bufsize should be set to zero when the end of file is reached.
- ** As a corollary, this function should always read at least one byte
- ** on each call, until the EOF condition is met.
- */
- dav_error * (*read_stream)(dav_stream *stream,
- void *buf, apr_size_t *bufsize);
-
/*
** Write data to the stream.
**
** is used to provide the repository with a way to set the headers
** in the response.
**
- ** It may be NULL if get_pathname is provided.
+ ** This function may be called without a following deliver(), to
+ ** handle a HEAD request.
+ **
+ ** This may be NULL if handle_get is FALSE.
*/
dav_error * (*set_headers)(request_rec *r,
const dav_resource *resource);
- /* Get a pathname for the file represented by the resource descriptor.
- * A provider may need to create a temporary copy of the file, if it is
- * not directly accessible in a filesystem. free_handle_p will be set by
- * the provider to point to information needed to clean up any temporary
- * storage used.
- *
- * Returns NULL if the file could not be made accessible.
- */
- const char * (*get_pathname)(
- const dav_resource *resource,
- void **free_handle_p
- );
-
- /* Free any temporary storage associated with a file made accessible by
- * get_pathname().
- */
- void (*free_file)(
- void *free_handle
- );
+ /*
+ ** The provider should deliver the resource into the specified filter.
+ ** Basically, this is the response to the GET method.
+ **
+ ** Note that this is called for all resources, including collections.
+ ** The provider should determine what has content to deliver or not.
+ **
+ ** set_headers will be called prior to this function, allowing the
+ ** provider to set the appropriate response headers.
+ **
+ ** This may be NULL if handle_get is FALSE.
+ ** ### maybe toss handle_get and just use this function as the marker
+ */
+ dav_error * (*deliver)(const dav_resource *resource,
+ ap_filter_t *output);
/* Create a collection resource. The resource must not already exist.
*