Annotation of embedaddon/libpdel/http/http_client.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * Copyright (c) 2001-2002 Packet Design, LLC.
                      4:  * All rights reserved.
                      5:  * 
                      6:  * Subject to the following obligations and disclaimer of warranty,
                      7:  * use and redistribution of this software, in source or object code
                      8:  * forms, with or without modifications are expressly permitted by
                      9:  * Packet Design; provided, however, that:
                     10:  * 
                     11:  *    (i)  Any and all reproductions of the source or object code
                     12:  *         must include the copyright notice above and the following
                     13:  *         disclaimer of warranties; and
                     14:  *    (ii) No rights are granted, in any manner or form, to use
                     15:  *         Packet Design trademarks, including the mark "PACKET DESIGN"
                     16:  *         on advertising, endorsements, or otherwise except as such
                     17:  *         appears in the above copyright notice or in the software.
                     18:  * 
                     19:  * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
                     20:  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
                     21:  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
                     22:  * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
                     23:  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
                     24:  * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
                     25:  * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
                     26:  * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
                     27:  * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
                     28:  * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
                     29:  * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
                     30:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
                     31:  * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
                     32:  * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
                     33:  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     34:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
                     35:  * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
                     36:  * THE POSSIBILITY OF SUCH DAMAGE.
                     37:  *
                     38:  * Author: Archie Cobbs <archie@freebsd.org>
                     39:  */
                     40: 
                     41: #include <sys/types.h>
                     42: #include <sys/socket.h>
                     43: #include <sys/syslog.h>
                     44: #include <sys/queue.h>
                     45: 
                     46: #include <netinet/in_systm.h>
                     47: #include <netinet/in.h>
                     48: #include <arpa/inet.h>
                     49: 
                     50: #include <assert.h>
                     51: #include <stdio.h>
                     52: #include <stdlib.h>
                     53: #include <stdarg.h>
                     54: #include <string.h>
                     55: #include <unistd.h>
                     56: #include <pthread.h>
                     57: 
                     58: #include <openssl/ssl.h>
                     59: #include <openssl/err.h>
                     60: 
                     61: #include "structs/structs.h"
                     62: #include "structs/type/array.h"
                     63: 
                     64: #include "io/ssl_fp.h"
                     65: #include "util/typed_mem.h"
                     66: 
                     67: #include "http/http_defs.h"
                     68: #include "http/http_server.h"
                     69: #include "http/http_internal.h"
                     70: 
                     71: #define HTTP_CLIENT_TIMEOUT    90      /* timeout in seconds */
                     72: 
                     73: /* Cleanup state for http_client_connect() */
                     74: struct http_client_connect_state {
                     75:        int     sock;
                     76: };
                     77: 
                     78: /* HTTP client */
                     79: struct http_client {
                     80:        char                    *user_agent;    /* client user agent name */
                     81:        SSL_CTX                 *ssl;           /* ssl context */
                     82:        http_logger_t           *logger;        /* error logging routine */
                     83:        struct http_connection_cache
                     84:                                *cache;         /* cached connections */
                     85:        u_int                   max_conn;       /* max number of connections */
                     86:        pthread_mutex_t         mutex;          /* mutex for "condvar" */
                     87:        pthread_cond_t          condvar;        /* connection condition var */
                     88:        u_int                   num_conn;       /* number active connections */
                     89:        TAILQ_HEAD(,http_client_connection)
                     90:                                list;           /* active connections */
                     91: };
                     92: 
                     93: /* HTTP client connection */
                     94: struct http_client_connection {
                     95:        struct http_client      *client;        /* client who owns me */
                     96:        struct sockaddr_in      peer;           /* peer address */
                     97:        struct http_connection  *conn;          /* connection to server */
                     98:        char                    *reason;        /* reason for failure */
                     99:        u_char                  got_response;   /* response read from server */
                    100:        TAILQ_ENTRY(http_client_connection)
                    101:                                next;           /* next in client list */
                    102: };
                    103: 
                    104: /* Internal functions */
                    105: static void    http_client_connect_cleanup(void *arg);
                    106: 
                    107: /*********************************************************************
                    108:                        HTTP_CLIENT FUNCTIONS
                    109: *********************************************************************/
                    110: 
                    111: /*
                    112:  * Create a new client.
                    113:  */
                    114: struct http_client *
                    115: http_client_create(struct pevent_ctx *ctx, const char *user_agent,
                    116:        u_int max_conn, u_int max_cache, u_int max_cache_idle,
                    117:        http_logger_t *logger)
                    118: {
                    119:        struct http_client *client;
                    120:        int got_mutex = 0;
                    121:        int got_cond = 0;
                    122: 
                    123:        /* Sanity check */
                    124:        if (max_conn <= max_cache) {
                    125:                errno = EINVAL;
                    126:                return (NULL);
                    127:        }
                    128: 
                    129:        /* Create new client */
                    130:        if ((client = MALLOC("http_client", sizeof(*client))) == NULL)
                    131:                return (NULL);
                    132:        memset(client, 0, sizeof(*client));
                    133:        client->logger = logger;
                    134:        client->max_conn = max_conn;
                    135:        TAILQ_INIT(&client->list);
                    136: 
                    137:        /* Copy user agent */
                    138:        if ((client->user_agent = STRDUP("http_client.user_agent",
                    139:            user_agent)) == NULL)
                    140:                goto fail;
                    141: 
                    142:        /* Initialize connection cache */
                    143:        if ((client->cache = _http_connection_cache_create(
                    144:            ctx, max_cache, max_cache_idle)) == NULL)
                    145:                goto fail;
                    146: 
                    147:        /* Initialize mutex */
                    148:        if ((errno = pthread_mutex_init(&client->mutex, NULL)) != 0)
                    149:                goto fail;
                    150:        got_mutex = 1;
                    151: 
                    152:        /* Initialize condition variable */
                    153:        if ((errno = pthread_cond_init(&client->condvar, NULL)) != 0)
                    154:                goto fail;
                    155:        got_cond = 1;
                    156: 
                    157:        /* Done */
                    158:        return (client);
                    159: 
                    160: fail:
                    161:        /* Clean up after failure */
                    162:        if (got_cond)
                    163:                pthread_cond_destroy(&client->condvar);
                    164:        if (got_mutex)
                    165:                pthread_mutex_destroy(&client->mutex);
                    166:        _http_connection_cache_destroy(&client->cache);
                    167:        FREE("http_client.user_agent", client->user_agent);
                    168:        return (NULL);
                    169: }
                    170: 
                    171: /*
                    172:  * Destroy an HTTP client.
                    173:  *
                    174:  * This will return -1 with errno = EBUSY if there are any
                    175:  * associated connections still active.
                    176:  */
                    177: int
                    178: http_client_destroy(struct http_client **clientp)
                    179: {
                    180:        struct http_client *client = *clientp;
                    181:        int r;
                    182: 
                    183:        /* Sanity */
                    184:        if (client == NULL)
                    185:                return (0);
                    186: 
                    187:        /* Acquire mutex */
                    188:        r = pthread_mutex_lock(&client->mutex);
                    189:        assert(r == 0);
                    190: 
                    191:        /* Check for active connections */
                    192:        if (client->num_conn != 0) {
                    193:                r = pthread_mutex_unlock(&client->mutex);
                    194:                assert(r == 0);
                    195:                errno = EBUSY;
                    196:                return (-1);
                    197:        }
                    198: 
                    199:        /* Shut it down */
                    200:        if (client->ssl != NULL)
                    201:                SSL_CTX_free(client->ssl);
                    202:        _http_connection_cache_destroy(&client->cache);
                    203:        r = pthread_mutex_unlock(&client->mutex);
                    204:        assert(r == 0);
                    205:        pthread_cond_destroy(&client->condvar);
                    206:        pthread_mutex_destroy(&client->mutex);
                    207:        FREE("http_client.user_agent", client->user_agent);
                    208:        FREE("http_client", client);
                    209:        *clientp = NULL;
                    210:        return (0);
                    211: }
                    212: 
                    213: /*********************************************************************
                    214:                HTTP_CLIENT_CONNECTION FUNCTIONS
                    215: *********************************************************************/
                    216: 
                    217: /*
                    218:  * Create a new HTTP connection object from this client.
                    219:  */
                    220: struct http_client_connection *
                    221: http_client_connect(struct http_client *client,
                    222:        struct in_addr ip, u_int16_t port, int https)
                    223: {
                    224:        struct http_client_connection *cc = NULL;
                    225:        struct http_client_connect_state state;
                    226:        struct sockaddr_in sin;
                    227:        FILE *fp = NULL;
                    228:        int sock = -1;
                    229:        int ret;
                    230:        int r;
                    231: 
                    232:        /* Acquire mutex, release if canceled */
                    233:        r = pthread_mutex_lock(&client->mutex);
                    234:        assert(r == 0);
                    235:        pthread_cleanup_push((void (*)(void *))pthread_mutex_unlock,
                    236:            &client->mutex);
                    237: 
                    238:        /* Initialize SSL context for this client if not already done */
                    239:        if (https && client->ssl == NULL) {
                    240: 
                    241:                /* Initialize SSL stuff */
                    242:                _http_ssl_init();
                    243: 
                    244:                /* Initialize new SSL context for this client */
                    245:                if ((client->ssl = SSL_CTX_new(
                    246:                    SSLv23_client_method())) == NULL) {
                    247:                        ssl_log(NULL, NULL);
                    248:                        goto fail;
                    249:                }
                    250:        }
                    251: 
                    252:        /* Set up peer address */
                    253:        memset(&sin, 0, sizeof(sin));
                    254: #if !defined(__linux__) && !defined(__sun__)
                    255:        sin.sin_len = sizeof(sin);
                    256: #endif
                    257:        sin.sin_family = AF_INET;
                    258:        sin.sin_addr = ip;
                    259:        sin.sin_port = htons(port);
                    260: 
                    261:        /* See if we have a cached connection to this server */
                    262:        if (client->cache != NULL
                    263:            && _http_connection_cache_get(client->cache, &sin,
                    264:              https ? client->ssl : NULL, &fp, &sock) == 0)
                    265:                goto connected;
                    266: 
                    267:        /* Wait if too many connections already exist */
                    268:        while (client->num_conn >= client->max_conn)
                    269:                pthread_cond_wait(&client->condvar, &client->mutex);
                    270: 
                    271:        /* Get socket */
                    272:        if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
                    273:                (*client->logger)(LOG_ERR,
                    274:                    "%s: %s", "socket", strerror(errno));
                    275:                goto fail;
                    276:        }
                    277: 
                    278:        /* Release mutex */
                    279:        r = pthread_mutex_unlock(&client->mutex);
                    280:        assert(r == 0);
                    281: 
                    282:        /* Don't leak socket if thread is canceled */
                    283:        state.sock = sock;
                    284:        pthread_cleanup_push(http_client_connect_cleanup, &state);
                    285: 
                    286:        /* Connect to peer */
                    287:        ret = connect(sock, (struct sockaddr *)&sin, sizeof(sin));
                    288: 
                    289:        /* Remove cleanup hook */
                    290:        pthread_cleanup_pop(0);
                    291: 
                    292:        /* Acquire mutex */
                    293:        r = pthread_mutex_lock(&client->mutex);
                    294:        assert(r == 0);
                    295: 
                    296:        /* Check if connected */
                    297:        if (ret == -1) {
                    298:                (*client->logger)(LOG_ERR, "failed to connect to %s:%u: %s",
                    299:                    inet_ntoa(ip), port, strerror(errno));
                    300:                goto fail;
                    301:        }
                    302: 
                    303: connected:
                    304:        /* Create new client connection object */
                    305:        if ((cc = MALLOC("http_client_connection", sizeof(*cc))) == NULL)
                    306:                goto fail;
                    307:        memset(cc, 0, sizeof(*cc));
                    308:        cc->client = client;
                    309:        cc->peer = sin;
                    310: 
                    311:        /* Create new connection */
                    312:        if ((cc->conn = _http_connection_create(fp, sock, 0, ip, port,
                    313:            client->ssl, client->logger, HTTP_CLIENT_TIMEOUT)) == NULL)
                    314:                goto fail;
                    315:        fp = NULL;
                    316:        sock = -1;
                    317: 
                    318:        /* Try to do keep-alive */
                    319:        cc->conn->keep_alive = 1;
                    320: 
                    321:        /* Add to client connection list */
                    322:        TAILQ_INSERT_TAIL(&client->list, cc, next);
                    323:        client->num_conn++;
                    324: 
                    325:        /* Set some default request headers */
                    326:        if (http_request_set_header(cc->conn->req, 0,
                    327:              HTTP_HEADER_USER_AGENT, "%s", client->user_agent) == -1
                    328:            || http_request_set_header(cc->conn->req, 0,
                    329:              HTTP_HEADER_HOST, "%s:%u", inet_ntoa(ip), port) == -1
                    330:            || http_request_set_header(cc->conn->req, 0,
                    331:              HTTP_HEADER_ACCEPT, "*/*") == -1
                    332:            || http_request_set_header(cc->conn->req, 0,
                    333:              HTTP_HEADER_ACCEPT_CHARSET, "iso-8859-1") == -1
                    334:            || http_request_set_header(cc->conn->req, 0,
                    335:              HTTP_HEADER_ACCEPT_ENCODING, "identity") == -1)
                    336:                goto fail;
                    337: 
                    338:        /* Done */
                    339:        goto done;
                    340: 
                    341: fail:
                    342:        /* Cleanup after failure */
                    343:        if (cc != NULL) {
                    344:                if (cc->conn != NULL)
                    345:                        _http_connection_free(&cc->conn);
                    346:                FREE("http_client_connection", cc);
                    347:                cc = NULL;
                    348:        }
                    349:        if (fp != NULL)
                    350:                fclose(fp);
                    351:        else if (sock != -1)
                    352:                (void)close(sock);
                    353: 
                    354: done:;
                    355:        /* Done */
                    356:        pthread_cleanup_pop(1);
                    357:        return (cc);
                    358: }
                    359: 
                    360: /*
                    361:  * Get local IP address.
                    362:  */
                    363: struct in_addr
                    364: http_client_get_local_ip(struct http_client_connection *cc)
                    365: {
                    366:        return (cc->conn->local_ip);
                    367: }
                    368: 
                    369: /*
                    370:  * Get local port.
                    371:  */
                    372: u_int16_t
                    373: http_client_get_local_port(struct http_client_connection *cc)
                    374: {
                    375:        return (cc->conn->local_port);
                    376: }
                    377: 
                    378: /*
                    379:  * Get request associated with client.
                    380:  */
                    381: struct http_request *
                    382: http_client_get_request(struct http_client_connection *cc)
                    383: {
                    384:        return (cc->conn->req);
                    385: }
                    386: 
                    387: /*
                    388:  * Get response associated with client connection.
                    389:  */
                    390: struct http_response *
                    391: http_client_get_response(struct http_client_connection *cc)
                    392: {
                    393:        struct http_request *const req = cc->conn->req;
                    394:        struct http_response *resp = cc->conn->resp;
                    395:        char buf[128];
                    396: 
                    397:        /* Already got it? */
                    398:        if (cc->got_response)
                    399:                return (resp);
                    400: 
                    401:        /* Send request headers (if not sent already) */
                    402:        if (http_request_send_headers(req) == -1) {
                    403:                snprintf(buf, sizeof(buf), "Error sending request: %s",
                    404:                    strerror(errno));
                    405:                return (NULL);
                    406:        }
                    407: 
                    408:        /* Send back body (it was buffered) */
                    409:        _http_message_send_body(req->msg);
                    410: 
                    411:        /* Read response from server */
                    412:        cc->got_response = 1;
                    413:        if (_http_response_read(cc->conn, buf, sizeof(buf)) == -1)
                    414:                resp = NULL;
                    415: 
                    416:        /* Save reason message */
                    417:        cc->reason = STRDUP("http_client_connection.reason", buf);
                    418:        return (resp);
                    419: }
                    420: 
                    421: /*
                    422:  * Close a client connection. The connection is cached if appropriate.
                    423:  */
                    424: void
                    425: http_client_close(struct http_client_connection **ccp)
                    426: {
                    427:        struct http_client_connection *const cc = *ccp;
                    428:        struct http_connection *conn;
                    429:        struct http_client *client;
                    430:        struct http_response *resp;
                    431:        int r;
                    432: 
                    433:        /* Sanity */
                    434:        if (cc == NULL)
                    435:                return;
                    436:        *ccp = NULL;
                    437: 
                    438:        /* Get client and connection */
                    439:        client = cc->client;
                    440:        conn = cc->conn;
                    441: 
                    442:        /* Acquire client mutex */
                    443:        r = pthread_mutex_lock(&client->mutex);
                    444:        assert(r == 0);
                    445: 
                    446:        /* Cache connection socket if appropriate */
                    447:        if (conn->keep_alive
                    448:            && client->cache != NULL
                    449:            && (resp = http_client_get_response(cc)) != NULL
                    450:            && _http_head_want_keepalive(resp->msg->head)
                    451:            && _http_connection_cache_put(client->cache,
                    452:              &cc->peer, client->ssl, conn->fp, conn->sock) == 0) {
                    453:                conn->fp = NULL;
                    454:                conn->sock = -1;
                    455:        }
                    456: 
                    457:        /* Remove from active connection list and wakeup next waiting thread */
                    458:        TAILQ_REMOVE(&client->list, cc, next);
                    459:        if (client->num_conn-- == client->max_conn)
                    460:                pthread_cond_signal(&client->condvar);
                    461: 
                    462:        /* Release client mutex */
                    463:        r = pthread_mutex_unlock(&client->mutex);
                    464:        assert(r == 0);
                    465: 
                    466:        /* Shutdown this connection */
                    467:        _http_connection_free(&cc->conn);
                    468:        FREE("http_client_connection.reason", cc->reason);
                    469:        FREE("http_client_connection", cc);
                    470: }
                    471: 
                    472: /*
                    473:  * Get response error/reason string.
                    474:  */
                    475: const char *
                    476: http_client_get_reason(struct http_client_connection *cc)
                    477: {
                    478:        if (cc->reason == NULL)
                    479:                return ("Uknown error");
                    480:        return (cc->reason);
                    481: }
                    482: 
                    483: /*********************************************************************
                    484:                        INTERNAL FUNCTIONS
                    485: *********************************************************************/
                    486: 
                    487: /*
                    488:  * Cleanup for http_client_connect() if thread is canceled.
                    489:  */
                    490: static void
                    491: http_client_connect_cleanup(void *arg)
                    492: {
                    493:        const struct http_client_connect_state *const state = arg;
                    494: 
                    495:        close(state->sock);
                    496: }
                    497: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>