These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / 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         int rc;
241
242         /* Identify scheme */
243         scheme = http_scheme ( uri );
244         if ( ! scheme )
245                 return -ENOTSUP;
246
247         /* Sanity check */
248         if ( ! uri->host )
249                 return -EINVAL;
250
251         /* Look for a reusable connection in the pool */
252         list_for_each_entry ( conn, &http_connection_pool, pool.list ) {
253
254                 /* Sanity checks */
255                 assert ( conn->uri != NULL );
256                 assert ( conn->uri->host != NULL );
257
258                 /* Reuse connection, if possible */
259                 if ( ( scheme == conn->scheme ) &&
260                      ( strcmp ( uri->host, conn->uri->host ) == 0 ) ) {
261
262                         /* Remove from connection pool, stop timer,
263                          * attach to parent interface, and return.
264                          */
265                         pool_del ( &conn->pool );
266                         intf_plug_plug ( &conn->xfer, xfer );
267                         DBGC2 ( conn, "HTTPCONN %p reused %s://%s\n",
268                                 conn, conn->scheme->name, conn->uri->host );
269                         return 0;
270                 }
271         }
272
273         /* Allocate and initialise structure */
274         conn = zalloc ( sizeof ( *conn ) );
275         ref_init ( &conn->refcnt, http_conn_free );
276         conn->uri = uri_get ( uri );
277         conn->scheme = scheme;
278         intf_init ( &conn->socket, &http_conn_socket_desc, &conn->refcnt );
279         intf_init ( &conn->xfer, &http_conn_xfer_desc, &conn->refcnt );
280         pool_init ( &conn->pool, http_conn_expired, &conn->refcnt );
281
282         /* Open socket */
283         memset ( &server, 0, sizeof ( server ) );
284         server.st_port = htons ( uri_port ( uri, scheme->port ) );
285         socket = &conn->socket;
286         if ( scheme->filter &&
287              ( ( rc = scheme->filter ( socket, uri->host, &socket ) ) != 0 ) )
288                 goto err_filter;
289         if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM,
290                                              ( struct sockaddr * ) &server,
291                                              uri->host, NULL ) ) != 0 )
292                 goto err_open;
293
294         /* Attach to parent interface, mortalise self, and return */
295         intf_plug_plug ( &conn->xfer, xfer );
296         ref_put ( &conn->refcnt );
297
298         DBGC2 ( conn, "HTTPCONN %p created %s://%s:%d\n", conn,
299                 conn->scheme->name, conn->uri->host, ntohs ( server.st_port ) );
300         return 0;
301
302  err_open:
303  err_filter:
304         DBGC2 ( conn, "HTTPCONN %p could not create %s://%s: %s\n",
305                 conn, conn->scheme->name, conn->uri->host, strerror ( rc ) );
306         http_conn_close ( conn, rc );
307         ref_put ( &conn->refcnt );
308         return rc;
309 }