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>