Skip to content

Instantly share code, notes, and snippets.

@moriyoshi
Created April 23, 2010 07:03
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save moriyoshi/376292 to your computer and use it in GitHub Desktop.
Save moriyoshi/376292 to your computer and use it in GitHub Desktop.
/*
* Copyright (c) Moriyoshi Koizumi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <httpd.h>
#include <http_config.h>
#include <http_core.h>
#include <http_log.h>
#include <http_main.h>
#include <http_protocol.h>
#include <http_request.h>
#include <util_script.h>
#include <http_connection.h>
#include "apr_strings.h"
#define JSON_MIME_TYPE "application/json"
#define JAVASCRIPT_MIME_TYPE "text/javascript"
module AP_MODULE_DECLARE_DATA jsonp_module;
typedef struct jsonp_req_ctx {
apr_table_t *query;
} jsonp_req_ctx_t;
static apr_table_t *parse_query(apr_pool_t *pool, const char* query)
{
apr_table_t *retval = apr_table_make(pool, 0);
const char *p = query;
while (*p) {
const char *n = strchr(p, '&'), *_n = n ? n: p + strlen(p);
char *chunk = apr_pstrndup(pool, p, _n - p);
char *s = strchr(chunk, '=');
if (s) {
*s = '\0';
ap_unescape_url(chunk);
ap_unescape_url(s + 1);
apr_table_setn(retval, chunk, s + 1);
} else {
ap_unescape_url(chunk);
apr_table_setn(retval, chunk, "");
}
if (!n)
p = _n;
else
p = n + 1;
}
return retval;
}
static apr_status_t jsonp_filter(ap_filter_t *f, apr_bucket_brigade *bb)
{
const char *callback = f->ctx;
apr_size_t l = strlen(f->r->content_type);
if (!f->r->content_type || l < sizeof(JSON_MIME_TYPE) - 1 ||
strncmp(f->r->content_type, JSON_MIME_TYPE,
sizeof(JSON_MIME_TYPE) - 1) != 0) {
return ap_pass_brigade(f->next, bb);
} else {
apr_bucket_brigade *_bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
apr_bucket *b;
APR_BRIGADE_INSERT_TAIL(_bb, apr_bucket_pool_create(callback, strlen(callback), f->r->pool, bb->bucket_alloc));
APR_BRIGADE_INSERT_TAIL(_bb, apr_bucket_immortal_create("(", 1, bb->bucket_alloc));
while ((b = APR_BRIGADE_FIRST(bb)) && b != APR_BRIGADE_SENTINEL(bb)) {
if (APR_BUCKET_IS_EOS(b)) {
APR_BRIGADE_INSERT_TAIL(_bb, apr_bucket_immortal_create(")", 1, bb->bucket_alloc));
}
APR_BUCKET_REMOVE(b);
APR_BRIGADE_INSERT_TAIL(_bb, b);
}
f->r->content_type = apr_pstrcat(f->r->pool, JAVASCRIPT_MIME_TYPE, f->r->content_type + sizeof(JSON_MIME_TYPE) - 1, NULL);
return ap_pass_brigade(f->next, _bb);
}
}
static int jsonp_req_init(request_rec *r)
{
jsonp_req_ctx_t *ctx = apr_palloc(r->pool, sizeof(jsonp_req_ctx_t));
ctx->query = NULL;
if (r->parsed_uri.query)
ctx->query = parse_query(r->pool, r->parsed_uri.query);
ap_set_module_config(r->request_config, &jsonp_module, ctx);
return OK;
}
static void jsonp_insert_filter(request_rec *r)
{
jsonp_req_ctx_t *ctx = ap_get_module_config(r->request_config, &jsonp_module);
const char *callback = ctx->query ? apr_table_get(ctx->query, "callback"): NULL;
if (callback) {
ap_add_output_filter("jsonp", (void*)callback, r, r->connection);
}
}
static void jsonp_register_hooks(apr_pool_t *p)
{
ap_hook_fixups(jsonp_req_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_insert_filter(jsonp_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
ap_register_output_filter("jsonp", jsonp_filter, NULL, AP_FTYPE_RESOURCE);
}
module AP_MODULE_DECLARE_DATA jsonp_module =
{
STANDARD20_MODULE_STUFF,
NULL, /* per-directory config creator */
NULL, /* dir config merger */
NULL, /* server config creator */
NULL, /* server config merger */
NULL, /* command table */
jsonp_register_hooks, /* set up other request processing hooks */
};
/*
* vim: sts=4 sw=4 ts=4 et
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment