[http] Add missing check for memory allocation failure
[ipxe.git] / src / net / tcp / httpconn.c
1 /*
2 * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 /**
27 * @file
28 *
29 * Hyper Text Transfer Protocol (HTTP) connection management
30 *
31 */
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <byteswap.h>
37 #include <ipxe/tcpip.h>
38 #include <ipxe/uri.h>
39 #include <ipxe/timer.h>
40 #include <ipxe/xfer.h>
41 #include <ipxe/open.h>
42 #include <ipxe/pool.h>
43 #include <ipxe/http.h>
44
45 /** HTTP pooled connection expiry time */
46 #define HTTP_CONN_EXPIRY ( 10 * TICKS_PER_SEC )
47
48 /** HTTP connection pool */
49 static LIST_HEAD ( http_connection_pool );
50
51 /**
52 * Identify HTTP scheme
53 *
54 * @v uri URI
55 * @ret scheme HTTP scheme, or NULL
56 */
57 static struct http_scheme * http_scheme ( struct uri *uri ) {
58 struct http_scheme *scheme;
59
60 /* Sanity check */
61 if ( ! uri->scheme )
62 return NULL;
63
64 /* Identify scheme */
65 for_each_table_entry ( scheme, HTTP_SCHEMES ) {
66 if ( strcmp ( uri->scheme, scheme->name ) == 0 )
67 return scheme;
68 }
69
70 return NULL;
71 }
72
73 /**
74 * Free HTTP connection
75 *
76 * @v refcnt Reference count
77 */
78 static void http_conn_free ( struct refcnt *refcnt ) {
79 struct http_connection *conn =
80 container_of ( refcnt, struct http_connection, refcnt );
81
82 /* Free connection */
83 uri_put ( conn->uri );
84 free ( conn );
85 }
86
87 /**
88 * Close HTTP connection
89 *
90 * @v conn HTTP connection
91 * @v rc Reason for close
92 */
93 static void http_conn_close ( struct http_connection *conn, int rc ) {
94
95 /* Remove from connection pool, if applicable */
96 pool_del ( &conn->pool );
97
98 /* Shut down interfaces */
99 intf_shutdown ( &conn->socket, rc );
100 intf_shutdown ( &conn->xfer, rc );
101 if ( rc == 0 ) {
102 DBGC2 ( conn, "HTTPCONN %p closed %s://%s\n",
103 conn, conn->scheme->name, conn->uri->host );
104 } else {
105 DBGC ( conn, "HTTPCONN %p closed %s://%s: %s\n",
106 conn, conn->scheme->name, conn->uri->host,
107 strerror ( rc ) );
108 }
109 }
110
111 /**
112 * Disconnect idle HTTP connection
113 *
114 * @v pool Pooled connection
115 */
116 static void http_conn_expired ( struct pooled_connection *pool ) {
117 struct http_connection *conn =
118 container_of ( pool, struct http_connection, pool );
119
120 /* Close connection */
121 http_conn_close ( conn, 0 /* Not an error to close idle connection */ );
122 }
123
124 /**
125 * Receive data from transport layer interface
126 *
127 * @v http HTTP connection
128 * @v iobuf I/O buffer
129 * @v meta Transfer metadata
130 * @ret rc Return status code
131 */
132 static int http_conn_socket_deliver ( struct http_connection *conn,
133 struct io_buffer *iobuf,
134 struct xfer_metadata *meta ) {
135
136 /* Mark connection as alive */
137 pool_alive ( &conn->pool );
138
139 /* Pass on to data transfer interface */
140 return xfer_deliver ( &conn->xfer, iobuf, meta );
141 }
142
143 /**
144 * Close HTTP connection transport layer interface
145 *
146 * @v http HTTP connection
147 * @v rc Reason for close
148 */
149 static void http_conn_socket_close ( struct http_connection *conn, int rc ) {
150
151 /* If we are reopenable (i.e. we are a recycled connection
152 * from the connection pool, and we have received no data from
153 * the underlying socket since we were pooled), then suggest
154 * that the client should reopen the connection.
155 */
156 if ( pool_is_reopenable ( &conn->pool ) )
157 pool_reopen ( &conn->xfer );
158
159 /* Close the connection */
160 http_conn_close ( conn, rc );
161 }
162
163 /**
164 * Recycle this connection after closing
165 *
166 * @v http HTTP connection
167 */
168 static void http_conn_xfer_recycle ( struct http_connection *conn ) {
169
170 /* Mark connection as recyclable */
171 pool_recyclable ( &conn->pool );
172 DBGC2 ( conn, "HTTPCONN %p keepalive enabled\n", conn );
173 }
174
175 /**
176 * Close HTTP connection data transfer interface
177 *
178 * @v conn HTTP connection
179 * @v rc Reason for close
180 */
181 static void http_conn_xfer_close ( struct http_connection *conn, int rc ) {
182
183 /* Add to the connection pool if keepalive is enabled and no
184 * error occurred.
185 */
186 if ( ( rc == 0 ) && pool_is_recyclable ( &conn->pool ) ) {
187 intf_restart ( &conn->xfer, rc );
188 pool_add ( &conn->pool, &http_connection_pool,
189 HTTP_CONN_EXPIRY );
190 DBGC2 ( conn, "HTTPCONN %p pooled %s://%s\n",
191 conn, conn->scheme->name, conn->uri->host );
192 return;
193 }
194
195 /* Otherwise, close the connection */
196 http_conn_close ( conn, rc );
197 }
198
199 /** HTTP connection socket interface operations */
200 static struct interface_operation http_conn_socket_operations[] = {
201 INTF_OP ( xfer_deliver, struct http_connection *,
202 http_conn_socket_deliver ),
203 INTF_OP ( intf_close, struct http_connection *,
204 http_conn_socket_close ),
205 };
206
207 /** HTTP connection socket interface descriptor */
208 static struct interface_descriptor http_conn_socket_desc =
209 INTF_DESC_PASSTHRU ( struct http_connection, socket,
210 http_conn_socket_operations, xfer );
211
212 /** HTTP connection data transfer interface operations */
213 static struct interface_operation http_conn_xfer_operations[] = {
214 INTF_OP ( pool_recycle, struct http_connection *,
215 http_conn_xfer_recycle ),
216 INTF_OP ( intf_close, struct http_connection *,
217 http_conn_xfer_close ),
218 };
219
220 /** HTTP connection data transfer interface descriptor */
221 static struct interface_descriptor http_conn_xfer_desc =
222 INTF_DESC_PASSTHRU ( struct http_connection, xfer,
223 http_conn_xfer_operations, socket );
224
225 /**
226 * Connect to an HTTP server
227 *
228 * @v xfer Data transfer interface
229 * @v uri Connection URI
230 * @ret rc Return status code
231 *
232 * HTTP connections are pooled. The caller should be prepared to
233 * receive a pool_reopen() message.
234 */
235 int http_connect ( struct interface *xfer, struct uri *uri ) {
236 struct http_connection *conn;
237 struct http_scheme *scheme;
238 struct sockaddr_tcpip server;
239 struct interface *socket;
240 unsigned int port;
241 int rc;
242
243 /* Identify scheme */
244 scheme = http_scheme ( uri );
245 if ( ! scheme )
246 return -ENOTSUP;
247
248 /* Sanity check */
249 if ( ! uri->host )
250 return -EINVAL;
251
252 /* Identify port */
253 port = uri_port ( uri, scheme->port );
254
255 /* Look for a reusable connection in the pool */
256 list_for_each_entry ( conn, &http_connection_pool, pool.list ) {
257
258 /* Sanity checks */
259 assert ( conn->uri != NULL );
260 assert ( conn->uri->host != NULL );
261
262 /* Reuse connection, if possible */
263 if ( ( scheme == conn->scheme ) &&
264 ( strcmp ( uri->host, conn->uri->host ) == 0 ) &&
265 ( port == uri_port ( conn->uri, scheme->port ) ) ) {
266
267 /* Remove from connection pool, stop timer,
268 * attach to parent interface, and return.
269 */
270 pool_del ( &conn->pool );
271 intf_plug_plug ( &conn->xfer, xfer );
272 DBGC2 ( conn, "HTTPCONN %p reused %s://%s:%d\n", conn,
273 conn->scheme->name, conn->uri->host, port );
274 return 0;
275 }
276 }
277
278 /* Allocate and initialise structure */
279 conn = zalloc ( sizeof ( *conn ) );
280 if ( ! conn ) {
281 rc = -ENOMEM;
282 goto err_alloc;
283 }
284 ref_init ( &conn->refcnt, http_conn_free );
285 conn->uri = uri_get ( uri );
286 conn->scheme = scheme;
287 intf_init ( &conn->socket, &http_conn_socket_desc, &conn->refcnt );
288 intf_init ( &conn->xfer, &http_conn_xfer_desc, &conn->refcnt );
289 pool_init ( &conn->pool, http_conn_expired, &conn->refcnt );
290
291 /* Open socket */
292 memset ( &server, 0, sizeof ( server ) );
293 server.st_port = htons ( port );
294 socket = &conn->socket;
295 if ( scheme->filter &&
296 ( ( rc = scheme->filter ( socket, uri->host, &socket ) ) != 0 ) )
297 goto err_filter;
298 if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM,
299 ( struct sockaddr * ) &server,
300 uri->host, NULL ) ) != 0 )
301 goto err_open;
302
303 /* Attach to parent interface, mortalise self, and return */
304 intf_plug_plug ( &conn->xfer, xfer );
305 ref_put ( &conn->refcnt );
306
307 DBGC2 ( conn, "HTTPCONN %p created %s://%s:%d\n", conn,
308 conn->scheme->name, conn->uri->host, port );
309 return 0;
310
311 err_open:
312 err_filter:
313 DBGC2 ( conn, "HTTPCONN %p could not create %s://%s:%d: %s\n", conn,
314 conn->scheme->name, conn->uri->host, port, strerror ( rc ) );
315 http_conn_close ( conn, rc );
316 ref_put ( &conn->refcnt );
317 err_alloc:
318 return rc;
319 }