├── .gitmodules ├── config ├── LICENSE ├── README.md └── ngx_http_mongodb_rest_module.c /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mongo-c-driver"] 2 | path = mongo-c-driver 3 | url = git://github.com/ctriolo/mongo-c-driver.git 4 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_mongodb_rest_module 2 | HTTP_MODULES="$HTTP_MODULES ngx_http_mongodb_rest_module" 3 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mongodb_rest_module.c $ngx_addon_dir/mongo-c-driver/src/*.c" 4 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/mongo-c-driver/src/*.h" 5 | CFLAGS="$CFLAGS --std=c99 -Isrc" 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2010 Khaja Minhajuddin 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nginx-mongodb-rest 2 | ========= 3 | An nginx module which works as a mongodb readonly rest client. 4 | 5 | Dependencies 6 | ============ 7 | **nginx-mongodb-rest** requires the mongo-c-driver which is a submodule of this 8 | repository. To checkout the submodule run 9 | 10 | $ git submodule init 11 | $ git submodule update 12 | 13 | 14 | Installation 15 | ============ 16 | Installing Nginx modules requires rebuilding Nginx from source: 17 | 18 | - Grab the [Nginx source](http://nginx.net/) and unpack it. 19 | - Clone this repository somewhere on your machine `git clone git://github.com/minhajuddin/nginx-mongodb-rest.git`. 20 | - Check out the required submodule, as described above. 21 | - Change to the directory containing the Nginx source. 22 | - Now build: 23 | 24 | $ ./configure --add-module=/path/to/nginx-gridfs/source/ 25 | $ make 26 | $ make install 27 | 28 | Configuration 29 | ============= 30 | 31 | location / { 32 | mongodb_rest "database_name" "collection_name"; 33 | } 34 | 35 | #example config 36 | location / { 37 | mongodb_rest "khalid_dev" "products"; 38 | } 39 | 40 | The above example configuration exposes the *products* collection in the 41 | *khalid_dev* database. If the server is running on your `localhost` the command 42 | `curl -i http://localhost/12` will give you the following output: 43 | 44 | HTTP/1.1 200 OK 45 | Server: nginx/0.8.54 46 | Date: Wed, 23 Feb 2011 23:23:11 GMT 47 | Content-Type: application/json 48 | Transfer-Encoding: chunked 49 | Connection: keep-alive 50 | 51 | {"_id" : "12" , "name": "funky widgets"} 52 | 53 | Known Issues / TODO / Things You Should Hack On 54 | =============================================== 55 | - allow passing *collection name* through the url 56 | - allow *ids* of different types e.g. *bson_oid*, *int* .. 57 | - allow *scriptable* collection names 58 | 59 | Authors 60 | ======= 61 | Khaja Minhajuddin (minhajuddin@cosmicvent.com), Nagaraju BVS (nagaraju@cosmicvent.com) 62 | 63 | Credits 64 | ======= 65 | - [nginx-gridfs](https://github.com/mdirolf/nginx-gridfs.git) was the "inspiration" for this project. 66 | 67 | License 68 | ======= 69 | **nginx-mongodb-rest** is licensed under the Apache License, Version 2.0. See *LICENSE* for more details. 70 | -------------------------------------------------------------------------------- /ngx_http_mongodb_rest_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "mongo-c-driver/src/bson.h" 8 | #include "mongo-c-driver/src/mongo.h" 9 | 10 | #define CONTENT_TYPE "application/json" 11 | #define MONGO_HOST "127.0.0.1" 12 | #define MONGO_PORT 27017 13 | 14 | static ngx_int_t ngx_http_mongodb_rest_handler(ngx_http_request_t* request); 15 | static char* ngx_http_mongodb_rest(ngx_conf_t* cf, ngx_command_t* command, void* void_conf); 16 | static char* ngx_http_mongodb_rest_merge_loc_conf(ngx_conf_t* cf, void* void_parent, void* void_child); 17 | static void* ngx_http_mongodb_rest_create_loc_conf(ngx_conf_t* conf); 18 | static void get(ngx_str_t* db, ngx_str_t* collection, char* id, ngx_buf_t* b); 19 | static void ngx_http_mongodb_rest_exit_worker(ngx_cycle_t* cycle); 20 | 21 | static ngx_int_t ngx_http_mongodb_rest_init_worker(ngx_cycle_t* cycle); 22 | 23 | //connection shared by requests for a single worker 24 | mongo_connection cached_connection[1]; 25 | 26 | typedef struct { 27 | ngx_str_t db; 28 | ngx_str_t collection; 29 | } ngx_http_mongodb_rest_loc_conf_t; 30 | 31 | 32 | static ngx_command_t ngx_http_mongodb_rest_commands[] = { 33 | { 34 | ngx_string("mongodb_rest"), 35 | NGX_HTTP_LOC_CONF | NGX_CONF_1MORE, 36 | ngx_http_mongodb_rest, 37 | NGX_HTTP_LOC_CONF_OFFSET, 38 | 0, 39 | NULL 40 | }, 41 | ngx_null_command 42 | }; 43 | 44 | 45 | static ngx_http_module_t ngx_http_mongodb_rest_module_ctx = { 46 | NULL, 47 | NULL, 48 | NULL, 49 | NULL, 50 | NULL, 51 | NULL, 52 | ngx_http_mongodb_rest_create_loc_conf, 53 | ngx_http_mongodb_rest_merge_loc_conf 54 | }; 55 | 56 | 57 | ngx_module_t ngx_http_mongodb_rest_module = { 58 | NGX_MODULE_V1, 59 | &ngx_http_mongodb_rest_module_ctx, 60 | ngx_http_mongodb_rest_commands, 61 | NGX_HTTP_MODULE, 62 | NULL, 63 | NULL, 64 | ngx_http_mongodb_rest_init_worker, 65 | NULL, 66 | NULL, 67 | ngx_http_mongodb_rest_exit_worker, 68 | NULL, 69 | NGX_MODULE_V1_PADDING 70 | }; 71 | 72 | 73 | static char* ngx_http_mongodb_rest(ngx_conf_t* cf, ngx_command_t* command, void* void_conf){ 74 | ngx_http_mongodb_rest_loc_conf_t *mr_conf = void_conf; 75 | ngx_http_core_loc_conf_t* core_conf; 76 | ngx_str_t *value; 77 | 78 | core_conf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); 79 | core_conf->handler = ngx_http_mongodb_rest_handler; 80 | 81 | value = cf->args->elts; 82 | 83 | mr_conf->db = value[1]; 84 | mr_conf->collection = value[2]; 85 | 86 | return NGX_CONF_OK; 87 | } 88 | 89 | 90 | static ngx_int_t ngx_http_mongodb_rest_handler(ngx_http_request_t* r){ 91 | ngx_buf_t *b; 92 | ngx_chain_t out; 93 | ngx_http_mongodb_rest_loc_conf_t *mr_conf; 94 | char id[100]; 95 | 96 | mr_conf = ngx_http_get_module_loc_conf(r, ngx_http_mongodb_rest_module); 97 | 98 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); 99 | 100 | if (b == NULL) { 101 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 102 | "Failed to allocate response buffer."); 103 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 104 | } 105 | 106 | /* set the headers of the response */ 107 | r->headers_out.status = NGX_HTTP_OK; 108 | r->headers_out.content_type.len = sizeof(CONTENT_TYPE) - 1; 109 | r->headers_out.content_type.data = (u_char *) CONTENT_TYPE; 110 | ngx_http_send_header(r); 111 | 112 | 113 | /* get id */ 114 | sscanf((char*)r->uri.data, "/%s", id); 115 | 116 | /* get the result and write it to the buffer */ 117 | get(&mr_conf->db, &mr_conf->collection, id, b); 118 | 119 | b->memory = 1; /* content is in read-only memory */ 120 | /* (i.e., filters should copy it rather than rewrite in place) */ 121 | b->last_buf = 1; /* there will be no more buffers in the request */ 122 | 123 | out.buf = b; 124 | out.next = NULL; 125 | 126 | return ngx_http_output_filter(r, &out); 127 | } 128 | 129 | static void* ngx_http_mongodb_rest_create_loc_conf(ngx_conf_t* conf){ 130 | ngx_http_mongodb_rest_loc_conf_t* mr_conf; 131 | 132 | mr_conf = ngx_pcalloc(conf->pool, sizeof(ngx_http_mongodb_rest_loc_conf_t)); 133 | return mr_conf; 134 | } 135 | 136 | 137 | static char* ngx_http_mongodb_rest_merge_loc_conf(ngx_conf_t* cf, void* void_parent, void* void_child){ 138 | ngx_http_mongodb_rest_loc_conf_t *parent = void_parent; 139 | ngx_http_mongodb_rest_loc_conf_t *child = void_child; 140 | 141 | ngx_conf_merge_str_value(child->db, parent->db, NULL); 142 | ngx_conf_merge_str_value(child->collection, parent->collection, NULL); 143 | 144 | return NGX_CONF_OK; 145 | } 146 | 147 | /* mongodb functions */ 148 | static ngx_int_t log_mongo_error(ngx_log_t *log, mongo_conn_return status); 149 | void to_json(char *buf, const char * data, int depth); 150 | 151 | static void get(ngx_str_t *db, ngx_str_t *collection, char* id, ngx_buf_t *b){ 152 | bson_buffer bb; 153 | bson obj; 154 | bson cond; 155 | char result[1000] = ""; 156 | char ns[1000]; 157 | 158 | //get the query 159 | bson_buffer_init(&bb); 160 | bson_append_string(&bb, "_id", id); 161 | bson_from_buffer(&cond, &bb); 162 | 163 | sprintf(ns, "%s.%s", db->data, collection->data); 164 | 165 | if(!mongo_find_one(cached_connection, ns, &cond, 0, &obj)){ 166 | strcpy(result, "{'error':'record not found'}"); 167 | } else { 168 | to_json(result, obj.data, 10); 169 | } 170 | 171 | b->pos = (u_char*)result; /* address of the first position of the data */ 172 | b->last = (u_char*)result + strlen(result); /* address of the last position of the data */ 173 | 174 | // destroy cond bb and other stuff 175 | bson_destroy(&cond); 176 | bson_destroy(&obj); 177 | } 178 | 179 | 180 | static ngx_int_t ngx_http_mongodb_rest_init_worker(ngx_cycle_t* cycle) { 181 | mongo_connection_options opts[1]; 182 | mongo_conn_return status; 183 | 184 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0,"WORKER INIT"); 185 | 186 | strcpy( opts->host , MONGO_HOST); 187 | opts->port = MONGO_PORT; 188 | 189 | status = mongo_connect( cached_connection, opts ); 190 | 191 | if (status != mongo_conn_success){ 192 | return log_mongo_error(cycle->log, status); 193 | } 194 | 195 | return NGX_OK; 196 | } 197 | 198 | 199 | static void ngx_http_mongodb_rest_exit_worker(ngx_cycle_t* cycle) { 200 | mongo_disconnect(cached_connection); 201 | mongo_destroy(cached_connection); 202 | } 203 | 204 | static ngx_int_t log_mongo_error(ngx_log_t *log, mongo_conn_return status){ 205 | 206 | switch (status) { 207 | case mongo_conn_success: 208 | return NGX_OK; 209 | case mongo_conn_bad_arg: 210 | ngx_log_error(NGX_LOG_ERR, log, 0, 211 | "Mongo Exception: Bad Arguments"); 212 | return NGX_ERROR; 213 | case mongo_conn_no_socket: 214 | ngx_log_error(NGX_LOG_ERR, log, 0, 215 | "Mongo Exception: No Socket"); 216 | return NGX_ERROR; 217 | case mongo_conn_fail: 218 | ngx_log_error(NGX_LOG_ERR, log, 0, "Mongo Exception: Connection Failure 127.0.0.1:27017;"); 219 | return NGX_ERROR; 220 | case mongo_conn_not_master: 221 | ngx_log_error(NGX_LOG_ERR, log, 0, 222 | "Mongo Exception: Not Master"); 223 | return NGX_ERROR; 224 | default: 225 | ngx_log_error(NGX_LOG_ERR, log, 0, 226 | "Mongo Exception: Unknown Error"); 227 | return NGX_ERROR; 228 | } 229 | } 230 | 231 | 232 | void to_json(char *buf, const char * data, int depth){ 233 | int array_count = 0; 234 | int object_count = 0; 235 | bson_iterator i; 236 | const char * key; 237 | 238 | char oidhex[25]; 239 | bson_iterator_init( &i , data ); 240 | 241 | sprintf(buf+strlen(buf),"{"); 242 | while ( bson_iterator_next( &i ) ){ 243 | bson_type t = bson_iterator_type( &i ); 244 | if ( t == 0 ) 245 | break; 246 | key = bson_iterator_key( &i ); 247 | 248 | if(object_count > 0){sprintf(buf+strlen(buf),",");} 249 | else{object_count=1;} 250 | 251 | sprintf(buf+strlen(buf), "\"%s\":" , key ); 252 | 253 | switch ( t ){ 254 | case bson_int: 255 | sprintf(buf+strlen(buf), "%d" , bson_iterator_int( &i ) ); 256 | break; 257 | case bson_double: 258 | sprintf(buf+strlen(buf), "%f" , bson_iterator_double( &i ) ); 259 | break; 260 | case bson_bool: 261 | sprintf(buf+strlen(buf), "%s" , bson_iterator_bool( &i ) ? "true" : "false" ); 262 | break; 263 | case bson_string: 264 | sprintf(buf+strlen(buf), "\"%s\"" , bson_iterator_string( &i ) ); 265 | break; 266 | case bson_null: 267 | sprintf( buf+strlen(buf),"null" ); 268 | break; 269 | case bson_oid: 270 | bson_oid_to_string(bson_iterator_oid(&i), oidhex); 271 | sprintf(buf+strlen(buf), "%s" , oidhex ); 272 | break; 273 | case bson_object: 274 | to_json(buf, bson_iterator_value( &i ) , depth + 1 ); 275 | break; 276 | case bson_array: 277 | sprintf(buf+strlen(buf), "[" ); 278 | bson_iterator j; 279 | bson_iterator_init( &j , bson_iterator_value(&i) ); 280 | array_count =0; 281 | while( bson_iterator_next(&j)){ 282 | if(array_count > 0){sprintf(buf+strlen(buf),",");} 283 | else{array_count=1;} 284 | to_json(buf, bson_iterator_value( &j ) , depth + 1 ); 285 | } 286 | sprintf(buf+strlen(buf), "]" ); 287 | break; 288 | 289 | default: 290 | fprintf( stderr , "can't print type : %d\n" , t ); 291 | 292 | } 293 | } 294 | sprintf(buf+strlen(buf),"}"); 295 | } 296 | --------------------------------------------------------------------------------