/Users/maurits/Documents/studie/afstuderen/biosphere/common/http_common.c

Go to the documentation of this file.
00001 /*
00002  * Author: MA Hartman
00003  * Date: apr 12, 2007
00004  * 
00005  * Function:
00006  * General HTTP network functions.
00007  * 
00008  * License information:
00009  * 
00010  * Copyright (c) 2006 Maurits Hartman
00011  *
00012  * Permission is hereby granted, free of charge, to any person
00013  * obtaining a copy of this software and associated documentation
00014  * files (the "Software"), to deal in the Software without
00015  * restriction, including without limitation the rights to use,
00016  * copy, modify, merge, publish, distribute, sublicense, and/or sell
00017  * copies of the Software, and to permit persons to whom the
00018  * Software is furnished to do so, subject to the following
00019  * conditions:
00020  * 
00021  * The above copyright notice and this permission notice shall be
00022  * included in all copies or substantial portions of the Software.
00023  * 
00024  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00025  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
00026  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00027  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
00028  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
00029  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00030  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00031  * OTHER DEALINGS IN THE SOFTWARE.
00032  * 
00033  */
00034  
00035 #include "http_common.h"
00036 #include "xml.h"
00037 #include <biosphere.h>
00038 
00039 #include <apr_strings.h>
00040 #include <apr_lib.h>
00041 #include <apr_network_io.h>
00042 #include <stdlib.h>
00043 
00044 
00045 bs_bool received_full_headers(const char *headerbuf)
00046 {
00047         unsigned i;
00048         apr_size_t headerlen;
00049         
00050         /* Scan the buffer to see if there is an header ending (\r\n\r\n or \n\n): */
00051         if (headerbuf == NULL) return FALSE;
00052         else headerlen = strlen(headerbuf);
00053         
00054         for (i = 0; i < headerlen - 2; i++) {
00055                 if (headerbuf[i] == '\n') {
00056                         if (headerbuf[i + 1] == '\r' && headerbuf[i + 2] == '\n')
00057                                 return TRUE;
00058                         else if (headerbuf[i + 1] == '\n')
00059                                 return TRUE;
00060                 }
00061         }
00062         return FALSE;
00063 }
00064 
00065 
00074 static bs_status extract_http_header_info(char *header,
00075                 http_header_info *hinfo, apr_pool_t *mp)
00076 {
00077         char *token, *q;
00078         
00079         /* Split the line: */
00080         token = apr_strtok(header, " :\r\n/", &q);
00081         if (apr_strnatcasecmp(token, "Content-Type") == 0) {
00082                 token = apr_strtok(NULL, " \r\n", &q);
00083                 hinfo->content_type = apr_pstrdup(mp, token);
00084         }
00085         else if (apr_strnatcasecmp(token, "Content-Length") == 0) {
00086                 token = apr_strtok(NULL, " \r\n", &q);
00087                 hinfo->content_length = apr_strtoi64(token, NULL, 0);
00088         }
00089         else if (apr_strnatcasecmp(token, "User-Agent") == 0) {
00090                 token = apr_strtok(NULL, "\r\n", &q);
00091                 if (token[0] == ' ') token++;
00092                 hinfo->user_agent = apr_pstrdup(mp, token);
00093         }
00094         else if (apr_strnatcmp(token, "HTTP") == 0) {
00095                 token = apr_strtok(NULL, " \r\n", &q); /* Remove HTTP version info */
00096                 token = apr_strtok(NULL, " \r\n", &q);
00097                 if (apr_isdigit(token[0]) && apr_isdigit(token[1]) && apr_isdigit(token[2]))
00098                         hinfo->http_code = (bs_uint32) strtol(token, NULL, 0);
00099         }
00100         
00101         return BS_OK;   
00102 }
00103 
00104 
00105 bs_status parse_http_headers(const char *headerbuf, apr_size_t headerlen,
00106                 http_header_info **info, apr_pool_t *mp)
00107 {
00108         unsigned buf_pos = 0;
00109         http_header_info *hinfo;
00110         
00111         hinfo = (http_header_info *) apr_pcalloc(mp, sizeof(http_header_info));
00112         
00113         /* Parse all the lines in the headerbuf: */
00114         while (1) {
00115                 unsigned begin_pos = buf_pos;
00116                 char *line = (char *) (headerbuf + buf_pos);
00117                 bs_status rv;
00118 
00119                 /* Extract a single line delimited by \n: */
00120                 while (headerbuf[buf_pos] != '\n' && buf_pos < headerlen) buf_pos++;
00121                 line = apr_pstrndup(mp, line, buf_pos - begin_pos);
00122                 rv = extract_http_header_info(line, hinfo, mp);
00123                 if (rv != BS_OK) return BS_HTTP_ERROR;
00124                 
00125                 /* Check to see if we found the end of the headers: */
00126                 if (buf_pos + 2 < headerlen && headerbuf[buf_pos + 1] == '\r' &&
00127                                 headerbuf[buf_pos + 2] == '\n') {
00128                         buf_pos += 3;
00129                         break;
00130                 }
00131                 else if (buf_pos + 1 < headerlen && headerbuf[buf_pos + 1] == '\n') {
00132                         buf_pos += 2;
00133                         break;  
00134                 }
00135                 else if (buf_pos >= headerlen) return BS_HTTP_ERROR;
00136                 
00137                 buf_pos++; /* For the line ending... */
00138         }
00139         
00140         /* Final checks and wrapup: */
00141         hinfo->content_offset = buf_pos;
00142         if (hinfo->content_length == 0 || hinfo->content_offset > hinfo->content_length)
00143                 return BS_HTTP_ERROR;
00144         *info = hinfo;
00145         return BS_OK;
00146 }
00147 
00148 
00159 static bs_status receive_response(apr_socket_t *sock, char **response_data,
00160                 bs_uint64 *length, apr_pool_t *mp)
00161 {
00162         bs_status rv;
00163         http_header_info *info;
00164         char *buf = NULL;
00165         int tries = 4; /* Number of tries to receive full HTTP headers */
00166         bs_uint64 remaining;
00167         
00168         /* First receiving until a buffer with complete header data is received: */
00169         while (!received_full_headers(buf) && tries > 0) {
00170                 apr_status_t rv2;
00171                 apr_size_t len;
00172                 char tmpbuf[HTTP_BUFFER_SIZE];
00173                 
00174                 /* Read from the socket and add to buffer: */
00175                 len = sizeof(tmpbuf) - 1; /* Always null-terminated */
00176                 memset(tmpbuf, '\0', sizeof(tmpbuf));
00177                 rv2 = apr_socket_recv(sock, tmpbuf, &len);
00178                 if (rv2 != APR_SUCCESS || len == 0) break;
00179                 
00180                 if (!buf) buf = apr_pstrdup(mp, tmpbuf);
00181                 else buf = apr_pstrcat(mp, buf, tmpbuf, NULL);
00182                 tries--;
00183         }
00184         
00185         /* Parse the HTTP headers: */
00186         if (!received_full_headers(buf)) return BS_HTTP_ERROR;
00187         rv = parse_http_headers(buf, strlen(buf), &info, mp);
00188         if (rv != BS_OK || !HTTP_CORRECT(info) || info->content_offset == 0)
00189                 return BS_HTTP_ERROR; /* TODO more checks */
00190         
00191         /* Continue receiving until EOF or all the bytes are received: */
00192         buf += info->content_offset;
00193         remaining = info->content_length - strlen(buf);
00194         while (remaining > 0) {
00195                 apr_status_t rv2;
00196                 apr_size_t len;
00197                 char tmpbuf[HTTP_BUFFER_SIZE];
00198                 
00199                 /* Read from the socket and add to buffer: */
00200                 len = (remaining > sizeof(tmpbuf) - 1) ? sizeof(tmpbuf) - 1 : remaining;
00201                 memset(tmpbuf, '\0', sizeof(tmpbuf));
00202                 rv2 = apr_socket_recv(sock, tmpbuf, &len);
00203                 if (rv2 != APR_SUCCESS || len == 0) break;
00204                 buf = apr_pstrcat(mp, buf, tmpbuf, NULL);
00205                 remaining -= len;
00206         }
00207         
00208         *length = strlen(buf);
00209         if (*length != info->content_length) return BS_HTTP_ERROR; /* TODO maybe too strict? */
00210 
00211         *response_data = buf;
00212         return BS_OK;
00213 }
00214 
00215 
00216 bs_status connect_and_post(const bs_service_request *request,
00217                 bs_service_response **response, const char *addr,
00218                 bs_uint16 port, apr_pool_t *mp)
00219 {
00220         bs_status rv;
00221         apr_status_t rv2;
00222         apr_sockaddr_t *sa;
00223         apr_socket_t *sock;
00224         char *buf, *xml, timebuf[APR_RFC822_DATE_LEN + 1];
00225         bs_uint64 size;
00226         apr_size_t size2;
00227         unsigned type;
00228         bs_service_response *resp = NULL;
00229         
00230         /* Create HTTP request in buffer: */
00231         rv = xlate_request_to_xml(&xml, &size, request, mp);
00232         if (rv != BS_OK) return rv;
00233         apr_rfc822_date(timebuf, apr_time_now());
00234         buf = apr_psprintf(mp,
00235                         "POST /service_request HTTP/1.0\r\n"
00236                         "User-Agent: " BIOSPHERE_VERSION_STRING "\r\n"
00237                         "Date: %s\r\n"
00238                         "Content-Length: %" BS_UINT64_FMT "\r\n"
00239                         "Content-Type: text/xml\r\n\r\n",
00240                         timebuf, size);
00241         buf = apr_pstrcat(mp, buf, xml, NULL);
00242         
00243         /* Connect to the daemon and send the data: */
00244         apr_sockaddr_info_get(&sa, addr, APR_INET, port, 0, mp);
00245         rv2 = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP, mp);
00246         if (rv2 != APR_SUCCESS) return BS_ERROR;
00247         rv2 = apr_socket_connect(sock, sa);
00248         if (rv2 != APR_SUCCESS) return BS_ERROR;
00249         size2 = strlen(buf);
00250         rv2 = apr_socket_send(sock, buf, &size2);
00251         if (rv2 != APR_SUCCESS || size == 0) return BS_ERROR;
00252         
00253         /* Wait for the response */
00254         rv = receive_response(sock, &buf, &size, mp);
00255         if (rv != BS_OK) return rv;
00256         rv = xlate_from_xml(buf, size, (void **) &resp, &type, mp);
00257         if (rv != BS_OK) return rv;
00258         
00259         apr_socket_close(sock);
00260         *response = resp;
00261         return BS_OK;   
00262 }
00263 
00264 
00265 bs_status connect_and_get(bs_definition **result, const char *addr,
00266                 bs_uint16 port, apr_pool_t *mp)
00267 {
00268         apr_status_t rv;
00269         apr_sockaddr_t *sa;
00270         apr_socket_t *sock;
00271         apr_size_t len;
00272         char *requestbuf, *xmlbuf, timebuf[APR_RFC822_DATE_LEN + 1];
00273         bs_status rv2;
00274         bs_uint64 size;
00275         unsigned type;
00276         bs_definition *def;
00277         
00278         /* Create a HTTP GET request for the daemon: */
00279         apr_rfc822_date(timebuf, apr_time_now());
00280         requestbuf = apr_psprintf(mp,
00281                         "GET /service_definitions HTTP/1.0\r\n"
00282                         "User-Agent: " BIOSPHERE_VERSION_STRING "\r\n"
00283                         "Date: %s\r\n\r\n",
00284                         timebuf);
00285         
00286         /* Connect to the daemon and send the data: */
00287         apr_sockaddr_info_get(&sa, "localhost", APR_INET, port, 0, mp);
00288         rv = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP, mp);
00289         if (rv != APR_SUCCESS) return BS_ERROR;
00290         rv = apr_socket_connect(sock, sa);
00291         if (rv != APR_SUCCESS) return BS_ERROR;
00292         len = strlen(requestbuf);
00293         rv = apr_socket_send(sock, requestbuf, &len);
00294         if (rv != APR_SUCCESS || len == 0) return BS_ERROR;
00295         
00296         /* Wait for the response, translate XML and close socket: */
00297         rv2 = receive_response(sock, &xmlbuf, &size, mp);
00298         if (rv2 != BS_OK) return rv;
00299         rv2 = xlate_from_xml(xmlbuf, size, (void **) &def, &type, mp);
00300         if (rv2 != BS_OK || type != SERVICE_DEF_FROM_XML) return rv;
00301         apr_socket_close(sock);
00302 
00303         *result = def;
00304         return BS_OK;           
00305 }

Generated on Tue Jul 17 09:50:52 2007 for Bio-SPHERE by  doxygen 1.5.1