Description:
An apache c module, that reads the request body from a PUT/POST request body, saves it on the disk and returns HTTP 201 (Created) status code if successful and returns the URI which the user can use to retrieve back the contents in "Location" response header and the name of the file in which the contents were saved in the response body.
Generating template for the module using 'apxs' tool:
Using the -g option of 'apxs' tool, we can generate a basic template of a module and add our logic to it.
#> /usr/local/apache2/bin/apxs -g -n putfile
Creating [DIR] putfile
Creating [FILE] putfile/Makefile
Creating [FILE] putfile/modules.mk
Creating [FILE] putfile/mod_putfile.c
Creating [FILE] putfile/.deps
The above command creates a template for a module named 'putfile' and a Makefile to compile.
Using the -g option of 'apxs' tool, we can generate a basic template of a module and add our logic to it.
#> /usr/local/apache2/bin/apxs -g -n putfile
Creating [DIR] putfile
Creating [FILE] putfile/Makefile
Creating [FILE] putfile/modules.mk
Creating [FILE] putfile/mod_putfile.c
Creating [FILE] putfile/.deps
The above command creates a template for a module named 'putfile' and a Makefile to compile.
Module source code:
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_log.h"
#include "ap_config.h"
#define BLOCKSIZE 4096
const char* documentRoot = "/usr/local/apache2/htdocs/";
int readAndWriteData(request_rec *r, int fd)
{
int ret = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
if(OK == ret && ap_should_client_block(r))
{
char* buffer = (char*)apr_pcalloc(r->pool, BLOCKSIZE);
int len;
while((len=ap_get_client_block(r, buffer, BLOCKSIZE)) > 0)
{
if(-1 == write(fd, buffer, len))
return -1;
}
return (-1 == len ? -1 : 0);
}
return -1;
}
static int putfile_handler(request_rec *r)
{
/*
* For each request, apache decides based on the 'SetHanlder' and 'AddHandler'
* directive, the module which would handle the request and populates the request_rec::handler
* field with the name of the module.
*
* A well behaving mo1dule should check if the value of this field matches with its name and
* process the request only if it does.
*/ if (strcmp(r->handler, "putfile")) {
return DECLINED;
}
/*
* This module supports only 'PUT'/'POST' requests.
*/ if(M_PUT != r->method_number && M_POST != r->method_number)
{
return DECLINED;
}
char fileName[200] = {'\0'};
struct timeval tv;
gettimeofday(&tv, NULL);
sprintf(fileName, "%sputfile/%ld.%ld", documentRoot, tv.tv_sec, tv.tv_usec);
int fd = creat(fileName, 0644);
if(-1 == fd)
{
return HTTP_INTERNAL_SERVER_ERROR;
}
close(fd);
if(0 != ret)
{
return HTTP_INTERNAL_SERVER_ERROR;
}
r->content_type = "text/plain";
/* Set response HTTP status */
r->status = HTTP_CREATED;
apr_table_add(r->headers_out, "Location", fileName + strlen(documentRoot));
ap_rputs(fileName + strlen(documentRoot), r);
return OK;
}
static void putfile_register_hooks(apr_pool_t *p)
{
ap_hook_handler(putfile_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA putfile_module = {
STANDARD20_MODULE_STUFF,
NULL, /* create per-dir config structures */
NULL, /* merge per-dir config structures */
NULL, /* create per-server config structures */
NULL, /* merge per-server config structures */
NULL, /* table of config file commands */
putfile_register_hooks /* register hooks */
};
Compiling and Installing the module:
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_log.h"
#include "ap_config.h"
#define BLOCKSIZE 4096
const char* documentRoot = "/usr/local/apache2/htdocs/";
int readAndWriteData(request_rec *r, int fd)
{
int ret = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
if(OK == ret && ap_should_client_block(r))
{
char* buffer = (char*)apr_pcalloc(r->pool, BLOCKSIZE);
int len;
while((len=ap_get_client_block(r, buffer, BLOCKSIZE)) > 0)
{
if(-1 == write(fd, buffer, len))
return -1;
}
return (-1 == len ? -1 : 0);
}
return -1;
}
static int putfile_handler(request_rec *r)
{
/*
* For each request, apache decides based on the 'SetHanlder' and 'AddHandler'
* directive, the module which would handle the request and populates the request_rec::handler
* field with the name of the module.
*
* A well behaving mo1dule should check if the value of this field matches with its name and
* process the request only if it does.
*/ if (strcmp(r->handler, "putfile")) {
return DECLINED;
}
/*
* This module supports only 'PUT'/'POST' requests.
*/ if(M_PUT != r->method_number && M_POST != r->method_number)
{
return DECLINED;
}
char fileName[200] = {'\0'};
struct timeval tv;
gettimeofday(&tv, NULL);
sprintf(fileName, "%sputfile/%ld.%ld", documentRoot, tv.tv_sec, tv.tv_usec);
int fd = creat(fileName, 0644);
if(-1 == fd)
{
return HTTP_INTERNAL_SERVER_ERROR;
}
close(fd);
if(0 != ret)
{
return HTTP_INTERNAL_SERVER_ERROR;
}
r->content_type = "text/plain";
/* Set response HTTP status */
r->status = HTTP_CREATED;
apr_table_add(r->headers_out, "Location", fileName + strlen(documentRoot));
ap_rputs(fileName + strlen(documentRoot), r);
return OK;
}
static void putfile_register_hooks(apr_pool_t *p)
{
ap_hook_handler(putfile_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA putfile_module = {
STANDARD20_MODULE_STUFF,
NULL, /* create per-dir config structures */
NULL, /* merge per-dir config structures */
NULL, /* create per-server config structures */
NULL, /* merge per-server config structures */
NULL, /* table of config file commands */
putfile_register_hooks /* register hooks */
};
Compiling and Installing the module:
Running the below command would compile the module and copy the shared library created to the modules directory (/usr/local/apache2/modules)
#> /usr/local/apache2/bin/apxs -c -i mod_putfile.c
Apache configuration:
LoadModule putfile_module modules/mod_putfile.so
<Location /putfile>
SetHandler putfile
</Location>
<Location /putfile>
SetHandler putfile
</Location>
Testing the module:
i) Create a sample file to send in the request.
#> echo "Sample Module" > input_file
#> echo "Sample Module" > input_file
#> cat input_file
Sample Module
Sample Module
ii) Send a PUT request.
#> curl -T ./input_file -D header http://127.0.0.1/putfile/
putfile/1378555086.385071
putfile/1378555086.385071
#> cat header
HTTP/1.1 100 Continue
HTTP/1.1 201 Created
Date: Sat, 07 Sep 2013 11:58:06 GMT
Server: Apache/2.2.22 (Unix) mod_ssl/2.2.22 OpenSSL/1.0.0e
Location: putfile/1378555086.385071
Content-Length: 25
Content-Type: text/plain
iii) Retrieve the file from the server.
#>wget http://127.0.0.1/putfile/1378555086.385071
--2013-09-07 17:29:19-- http://127.0.0.1/putfile/1378555086.385071
Connecting to 127.0.0.1:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 14 [text/plain]
Saving to: `1378555086.385071'
100%[===========================================================================================================>] 14 --.-K/s in 0s
2013-09-07 17:29:19 (754 KB/s) - `1378555086.385071' saved [14/14]
#> cat 1378555086.385071
Sample Module
--2013-09-07 17:29:19-- http://127.0.0.1/putfile/1378555086.385071
Connecting to 127.0.0.1:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 14 [text/plain]
Saving to: `1378555086.385071'
100%[===========================================================================================================>] 14 --.-K/s in 0s
2013-09-07 17:29:19 (754 KB/s) - `1378555086.385071' saved [14/14]
#> cat 1378555086.385071
Sample Module
No comments:
Post a Comment