| /* |
| * Copyright 1999-2004 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @author Pier Fumagalli <mailto:pier@betaversion.org> |
| * @version $Id$ |
| */ |
| |
| #include <httpd.h> |
| #include <http_request.h> |
| #include <http_config.h> |
| #include <http_core.h> |
| #include <http_log.h> |
| #include <http_main.h> |
| #include <http_protocol.h> |
| #include <util_script.h> |
| #include <wa.h> |
| #include <apr_tables.h> |
| |
| /* ************************************************************************* */ |
| /* GENERIC DECLARATIONS */ |
| /* ************************************************************************* */ |
| |
| /* Module declaration */ |
| module AP_MODULE_DECLARE_DATA webapp_module; |
| /* Wether the WebApp Library has been initialized or not */ |
| static wa_boolean wam_initialized=wa_false; |
| /* The list of configured connections */ |
| static wa_chain *wam_connections=NULL; |
| /* The main server using for logging error not related to requests */ |
| static server_rec *server=NULL; |
| |
| /* ************************************************************************* */ |
| /* MODULE AND LIBRARY INITIALIZATION AND DESTRUCTION */ |
| /* ************************************************************************* */ |
| |
| static int wam_module_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, |
| server_rec *s) { |
| ap_add_version_component(p, WA_VERSION); |
| return OK; |
| } |
| |
| /* Destroy the module and the WebApp Library */ |
| static apr_status_t wam_child_destroy(void *data) {/*void *nil) { */ |
| if (!wam_initialized) return APR_SUCCESS; |
| wa_shutdown(); |
| wam_initialized=wa_false; |
| return APR_SUCCESS; |
| } |
| |
| /* Startup the module and the WebApp Library */ |
| static void wam_child_init(apr_pool_t *p, server_rec *s) { |
| if (!wam_initialized) return; |
| server=s; |
| wa_startup(); |
| apr_pool_cleanup_register(p, NULL, wam_child_destroy, apr_pool_cleanup_null); |
| } |
| |
| /* Initialize the module and the WebApp Library */ |
| static const char *wam_init(apr_pool_t *p) { |
| const char *ret=NULL; |
| |
| if(wam_initialized==wa_true) return(NULL); |
| if ((ret=wa_init())!=NULL) return(ret); |
| wam_initialized=wa_true; |
| return(NULL); |
| } |
| |
| /* ************************************************************************* */ |
| /* CONFIGURATION DIRECTIVES HANDLING */ |
| /* ************************************************************************* */ |
| |
| /* Retrieve or create a wa_virtualhost structure for an Apache server_rec |
| and store it as the per-server module configuration */ |
| static const char *wam_server(server_rec *svr, wa_virtualhost **h) { |
| wa_virtualhost *host=NULL; |
| char *name=svr->server_hostname; |
| int port=(int)svr->port; |
| const char *ret=NULL; |
| |
| /* Attempt to retrieve the wa_virtualhost structure and create it |
| if necessary, storing it into the server_rec structure. */ |
| host=ap_get_module_config(svr->module_config,&webapp_module); |
| |
| /* If we already configured the wa_virtualhost, simply return it */ |
| if (host!=NULL) { |
| *h=host; |
| return(NULL); |
| } |
| |
| /* The wa_virtualhost was not found in the per-server module configuration |
| so we'll try to create it. */ |
| ret=wa_cvirtualhost(&host,name,port); |
| if (ret!=NULL) { |
| *h=NULL; |
| return(ret); |
| } |
| |
| /* We successfully created a wa_virtualhost structure, store it in the |
| per-server configuration member and return it. */ |
| ap_set_module_config(svr->module_config,&webapp_module,host); |
| *h=host; |
| return(NULL); |
| } |
| |
| /* Process the WebAppConnection directive. */ |
| static const char *wam_directive_connection(cmd_parms *cmd, void *mconfig, |
| const char *name, const char *prov, const char *p) { |
| wa_connection *conn=NULL; |
| const char *ret=NULL; |
| wa_chain *elem=NULL; |
| |
| /* Initialize the library */ |
| if ((ret=wam_init(cmd->pool))!=NULL) return(ret); |
| |
| /* Attempt to create a new wa_connection structure */ |
| if ((ret=wa_cconnection(&conn,name,prov,p))!=NULL) return(ret); |
| |
| /* Check if we have a duplicate connection with this name */ |
| elem=wam_connections; |
| while (elem!=NULL) { |
| wa_connection *curr=(wa_connection *)elem->curr; |
| if (strcasecmp(conn->name,curr->name)==0) |
| return("Duplicate connection name"); |
| elem=elem->next; |
| } |
| |
| /* We don't have a duplicate connection, store it locally */ |
| elem=apr_palloc(wa_pool,sizeof(wa_chain)); |
| elem->curr=conn; |
| elem->next=wam_connections; |
| wam_connections=elem; |
| return(NULL); |
| } |
| |
| /* Process the WebAppDeploy directive */ |
| static const char *wam_directive_deploy(cmd_parms *cmd, void *mconfig, |
| const char *name, const char *cnam, const char *path) { |
| wa_virtualhost *host=NULL; |
| wa_application *appl=NULL; |
| wa_connection *conn=NULL; |
| wa_chain *elem=NULL; |
| const char *ret=NULL; |
| |
| /* Initialize the library and retrieve/create the host structure */ |
| if ((ret=wam_init(cmd->pool))!=NULL) return(ret); |
| if ((ret=wam_server(cmd->server,&host))!=NULL) return(ret); |
| |
| /* Retrieve the connection */ |
| elem=wam_connections; |
| while(elem!=NULL) { |
| wa_connection *curr=(wa_connection *)elem->curr; |
| if (strcasecmp(curr->name,cnam)==0) { |
| conn=curr; |
| break; |
| } |
| elem=elem->next; |
| } |
| if (conn==NULL) return("Specified connection not configured"); |
| |
| /* Create a new wa_application member */ |
| if ((ret=wa_capplication(&appl,name,path))!=NULL) return(ret); |
| |
| /* Deploy the web application */ |
| if ((ret=wa_deploy(appl,host,conn))!=NULL) return(ret); |
| |
| /* Done */ |
| return(NULL); |
| } |
| |
| /* Process the WebAppInfo directive */ |
| static const char *wam_directive_info(cmd_parms *cmd, void *mconfig, |
| const char *path) { |
| const char *ret; |
| |
| /* We simply divert this call to a WebAppConnection and a WebAppDeploy |
| calls */ |
| if ((ret=wam_directive_connection(cmd,mconfig,"_INFO_","info",""))!=NULL) |
| return(ret); |
| if ((ret=wam_directive_deploy(cmd,mconfig,"_INFO_","_INFO_",path))!=NULL) |
| return(ret); |
| |
| return(NULL); |
| } |
| |
| /* The list of Directives for the WebApp module */ |
| static const command_rec wam_directives[] = { |
| AP_INIT_TAKE1( |
| "WebAppInfo", /* directive name */ |
| wam_directive_info, /* config action routine */ |
| NULL, /* argument to include in call */ |
| OR_OPTIONS, /* where available */ |
| "<uri-path>"), |
| AP_INIT_TAKE23( |
| "WebAppConnection", /* directive name */ |
| wam_directive_connection, /* config action routine */ |
| NULL, /* argument to include in call */ |
| RSRC_CONF, /* where available */ |
| "<name> <provider> [optional parameter]"), |
| AP_INIT_TAKE3( |
| "WebAppDeploy", /* directive name */ |
| wam_directive_deploy, /* config action routine */ |
| NULL, /* argument to include in call */ |
| RSRC_CONF, /* where available */ |
| "<name> <connection> <uri-path>"), |
| {NULL} |
| |
| }; |
| |
| /* ************************************************************************* */ |
| /* CALLBACKS TO WEB SERVER */ |
| /* ************************************************************************* */ |
| |
| /* Log a generic error */ |
| void wa_log(const char *f, const int l, const char *fmt, ...) { |
| va_list ap; |
| char buf[1024]; |
| #ifdef DEBUG |
| char tmp[1024]; |
| #endif |
| |
| va_start(ap,fmt); |
| #ifdef DEBUG |
| apr_vsnprintf(tmp,1024,fmt,ap); |
| apr_snprintf(buf,1024,"[%s:%d] %s",f,l,tmp); |
| #else |
| apr_vsnprintf(buf,1024,fmt,ap); |
| #endif |
| va_end(ap); |
| |
| ap_log_error(f,l,APLOG_NOERRNO|APLOG_ERR,0,server,"%s",buf); |
| } |
| |
| /* Log a message associated with a request */ |
| static void wam_handler_log(wa_request *r, const char *f, const int l, char *msg) { |
| request_rec *req=(request_rec *)r->data; |
| server_rec *svr=req->server; |
| |
| ap_log_error(f,l,APLOG_NOERRNO|APLOG_ERR,0,svr,"%s",msg); |
| } |
| |
| /* Set the HTTP status of the response. */ |
| static void wam_handler_setstatus(wa_request *r, int status, char *message) { |
| request_rec *req=(request_rec *)r->data; |
| |
| if ((message!=NULL) && (message[0]!='\0')) |
| req->status_line=apr_psprintf(req->pool,"%03d %s", status, message); |
| |
| req->status=status; |
| } |
| |
| /* Set the MIME Content-Type of the response. */ |
| static void wam_handler_setctype(wa_request *r, char *type) { |
| request_rec *req=(request_rec *)r->data; |
| |
| if (type==NULL) return; |
| |
| /* req->content_type=apr_pstrdup(req->pool,type); */ |
| /* It should be done like this in Apache 2.0 */ |
| /* This way, Apache 2.0 will be able to set the output filter */ |
| /* and it make webapp useable with deflate using AddOutputFilterByType DEFLATE text/html */ |
| ap_set_content_type(req, apr_pstrdup(req->pool,type)); |
| |
| apr_table_add(req->headers_out,"Content-Type",apr_pstrdup(req->pool,type)); |
| } |
| |
| /* Set a header in the HTTP response. */ |
| static void wam_handler_setheader(wa_request *r, char *name, char *value) { |
| request_rec *req=(request_rec *)r->data; |
| |
| if (name==NULL) return; |
| if (value==NULL) value=""; |
| |
| apr_table_add(req->headers_out,apr_pstrdup(req->pool,name), |
| apr_pstrdup(req->pool,value)); |
| } |
| |
| /* Commit the first part of the response (status and headers) */ |
| static void wam_handler_commit(wa_request *r) { |
| #if 0 |
| /* Modules can completely forget about headers in Apache 2.0, the |
| * core server always makes sure they are sent at the correct time. rbb |
| */ |
| request_rec *req=(request_rec *)r->data; |
| |
| ap_send_http_header(req); |
| ap_rflush(req); |
| #endif |
| } |
| |
| /* Flush all data in the response buffer */ |
| static void wam_handler_flush(wa_request *r) { |
| request_rec *req=(request_rec *)r->data; |
| |
| ap_rflush(req); |
| } |
| |
| /* Read a chunk of text from the request body */ |
| static int wam_handler_read(wa_request *r, char *buf, int len) { |
| request_rec *req=(request_rec *)r->data; |
| long ret=0; |
| |
| /* Check if we have something to read. */ |
| if (r->clen==0) return(0); |
| |
| /* Check if we had an error previously. */ |
| if (r->rlen==-1) return(-1); |
| |
| /* Send HTTP_CONTINUE to client when we're ready to read for the first |
| time. */ |
| if (r->rlen==0) { |
| if (ap_should_client_block(req)==0) return(0); |
| } |
| |
| /* Read some data from the client and fill the buffer. */ |
| ret=ap_get_client_block(req,buf,len); |
| if (ret==-1) { |
| r->rlen=-1; |
| return(-1); |
| } |
| |
| /* We did read some bytes, increment the current rlen and return. */ |
| r->rlen+=ret; |
| return((int)ret); |
| } |
| |
| /* Write a chunk of text into the response body. */ |
| static int wam_handler_write(wa_request *r, char *buf, int len) { |
| request_rec *req=(request_rec *)r->data; |
| |
| return(ap_rwrite(buf, len, req)); |
| } |
| |
| /* The structure holding all callback handling functions for the library */ |
| static wa_handler wam_handler = { |
| wam_handler_log, |
| wam_handler_setstatus, |
| wam_handler_setctype, |
| wam_handler_setheader, |
| wam_handler_commit, |
| wam_handler_flush, |
| wam_handler_read, |
| wam_handler_write, |
| }; |
| |
| /* ************************************************************************* */ |
| /* REQUEST HANDLING */ |
| /* ************************************************************************* */ |
| |
| /* Match an Apache request */ |
| static int wam_match(request_rec *r) { |
| wa_virtualhost *host=NULL; |
| wa_application *appl=NULL; |
| wa_chain *elem=NULL; |
| |
| /* Paranoid check */ |
| if (!wam_initialized) return(DECLINED); |
| |
| /* Check if this host was recognized */ |
| host=ap_get_module_config(r->server->module_config,&webapp_module); |
| if (host==NULL) return(DECLINED); |
| |
| /* Check if the uri is contained in one of our applications root path */ |
| elem=host->apps; |
| while(elem!=NULL) { |
| appl=(wa_application *)elem->curr; |
| if (strncmp(appl->rpth,r->uri,strlen(appl->rpth))==0) break; |
| |
| appl=NULL; |
| elem=elem->next; |
| } |
| if (appl==NULL) return(DECLINED); |
| |
| /* The uri path is matched: set the handler and return */ |
| r->handler=apr_pstrdup(r->pool,"webapp-handler"); |
| apr_table_setn(r->notes,"webapp-handler", "webapp-handler"); |
| |
| /* Set the webapp request structure into Apache's request structure */ |
| ap_set_module_config(r->request_config, &webapp_module, appl); |
| return(OK); |
| } |
| |
| /* Handle the current request */ |
| static int wam_invoke(request_rec *r) { |
| server_rec *svr=r->server; |
| conn_rec *con=r->connection; |
| wa_application *appl=NULL; |
| wa_request *req=NULL; |
| const char *msg=NULL; |
| char *stmp=NULL; |
| char *ctmp=NULL; |
| int ret=0; |
| apr_port_t port; |
| |
| if (strcmp(r->handler, "webapp-handler")) return(DECLINED); |
| |
| /* Paranoid check */ |
| if (!wam_initialized) return(DECLINED); |
| |
| /* Try to get a hold on the webapp request structure */ |
| appl=(wa_application *)ap_get_module_config(r->request_config, |
| &webapp_module); |
| if (appl==NULL) return(DECLINED); |
| |
| /* Allocate the webapp request structure */ |
| if ((msg=wa_ralloc(&req, &wam_handler, r))!=NULL) { |
| ap_log_error(APLOG_MARK,APLOG_NOERRNO|APLOG_ERR,0,svr,"%s",msg); |
| return(HTTP_INTERNAL_SERVER_ERROR); |
| } |
| |
| /* Set up the WebApp Library request structure client and server host |
| data (from the connection */ |
| stmp=(char *)r->hostname; |
| if (stmp==NULL) req->serv->host=""; |
| else req->serv->host=apr_pstrdup(req->pool,stmp); |
| |
| ctmp=(char *)ap_get_remote_host(con,r->per_dir_config, REMOTE_NAME, NULL); |
| if (ctmp==NULL) req->clnt->host=""; |
| else req->clnt->host=apr_pstrdup(req->pool,ctmp); |
| |
| req->serv->addr=apr_pstrdup(req->pool,con->local_ip); |
| req->clnt->addr=apr_pstrdup(req->pool,con->remote_ip); |
| apr_sockaddr_port_get(&port, con->local_addr); |
| req->serv->port= (int) port; |
| apr_sockaddr_port_get(&port, con->remote_addr); |
| req->clnt->port= (int) port; |
| |
| /* Set up all other members of the request structure */ |
| req->meth=apr_pstrdup(req->pool,(char *)r->method); |
| req->ruri=apr_pstrdup(req->pool,r->uri); |
| req->args=apr_pstrdup(req->pool,r->args); |
| req->prot=apr_pstrdup(req->pool,r->protocol); |
| req->schm=apr_pstrdup(req->pool,ap_http_method(r)); |
| req->user=apr_pstrdup(req->pool,r->user); |
| req->auth=apr_pstrdup(req->pool,r->ap_auth_type); |
| req->clen=0; |
| req->ctyp="\0"; |
| req->rlen=0; |
| |
| /* Copy headers into webapp request structure */ |
| if (r->headers_in!=NULL) { |
| apr_array_header_t *arr=apr_table_elts(r->headers_in); |
| apr_table_entry_t *ele=(apr_table_entry_t *)arr->elts; |
| int x=0; |
| |
| /* Copy headers one by one */ |
| for (x=0; x<arr->nelts;x++) { |
| if (ele[x].key==NULL) continue; |
| if (ele[x].val==NULL) continue; |
| apr_table_add(req->hdrs,apr_pstrdup(req->pool,ele[x].key), |
| apr_pstrdup(req->pool,ele[x].val)); |
| if (strcasecmp(ele[x].key,"Content-Length")==0) |
| req->clen=atol(ele[x].val); |
| if (strcasecmp(ele[x].key,"Content-Type")==0) |
| req->ctyp=apr_pstrdup(req->pool,ele[x].val); |
| } |
| } |
| |
| /* Check if we can read something from the request */ |
| ret=ap_setup_client_block(r,REQUEST_CHUNKED_DECHUNK); |
| if (ret!=OK) return(ret); |
| |
| /* Invoke the request */ |
| ret=wa_rinvoke(req,appl); |
| |
| /* Destroy the request member */ |
| wa_rfree(req); |
| ap_rflush(r); |
| |
| return(OK); |
| } |
| |
| /* bypass the directory_walk and file_walk for non-file requests */ |
| static int wam_map_to_storage(request_rec *r) |
| { |
| if (apr_table_get(r->notes, "webapp-handler")) { |
| r->filename = (char *)apr_filename_of_pathname(r->uri); |
| return OK; |
| } |
| return DECLINED; |
| } |
| |
| static void wam_hooks(apr_pool_t *p) |
| { |
| ap_hook_post_config(wam_module_init, NULL, NULL, APR_HOOK_MIDDLE); |
| ap_hook_child_init(wam_child_init, NULL, NULL, APR_HOOK_MIDDLE); |
| ap_hook_map_to_storage(wam_map_to_storage, NULL, NULL, APR_HOOK_MIDDLE); |
| ap_hook_handler(wam_invoke, NULL, NULL, APR_HOOK_MIDDLE); |
| ap_hook_translate_name(wam_match, NULL, NULL, APR_HOOK_MIDDLE); |
| } |
| |
| /* Apache module declaration */ |
| module AP_MODULE_DECLARE_DATA webapp_module = { |
| STANDARD20_MODULE_STUFF, |
| NULL, /* per-directory config creator */ |
| NULL, /* dir config merger */ |
| NULL, /* server config creator */ |
| NULL, /* server config merger */ |
| wam_directives, /* command table */ |
| wam_hooks, |
| }; |