Annotation of embedaddon/libpdel/http/http_client.c, revision 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>