You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

httpconn.c 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  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. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  24. /**
  25. * @file
  26. *
  27. * Hyper Text Transfer Protocol (HTTP) connection management
  28. *
  29. */
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <errno.h>
  33. #include <byteswap.h>
  34. #include <ipxe/tcpip.h>
  35. #include <ipxe/uri.h>
  36. #include <ipxe/timer.h>
  37. #include <ipxe/xfer.h>
  38. #include <ipxe/open.h>
  39. #include <ipxe/pool.h>
  40. #include <ipxe/http.h>
  41. /** HTTP pooled connection expiry time */
  42. #define HTTP_CONN_EXPIRY ( 10 * TICKS_PER_SEC )
  43. /** HTTP connection pool */
  44. static LIST_HEAD ( http_connection_pool );
  45. /**
  46. * Identify HTTP scheme
  47. *
  48. * @v uri URI
  49. * @ret scheme HTTP scheme, or NULL
  50. */
  51. static struct http_scheme * http_scheme ( struct uri *uri ) {
  52. struct http_scheme *scheme;
  53. /* Sanity check */
  54. if ( ! uri->scheme )
  55. return NULL;
  56. /* Identify scheme */
  57. for_each_table_entry ( scheme, HTTP_SCHEMES ) {
  58. if ( strcmp ( uri->scheme, scheme->name ) == 0 )
  59. return scheme;
  60. }
  61. return NULL;
  62. }
  63. /**
  64. * Free HTTP connection
  65. *
  66. * @v refcnt Reference count
  67. */
  68. static void http_conn_free ( struct refcnt *refcnt ) {
  69. struct http_connection *conn =
  70. container_of ( refcnt, struct http_connection, refcnt );
  71. /* Free connection */
  72. uri_put ( conn->uri );
  73. free ( conn );
  74. }
  75. /**
  76. * Close HTTP connection
  77. *
  78. * @v conn HTTP connection
  79. * @v rc Reason for close
  80. */
  81. static void http_conn_close ( struct http_connection *conn, int rc ) {
  82. /* Remove from connection pool, if applicable */
  83. pool_del ( &conn->pool );
  84. /* Shut down interfaces */
  85. intf_shutdown ( &conn->socket, rc );
  86. intf_shutdown ( &conn->xfer, rc );
  87. if ( rc == 0 ) {
  88. DBGC2 ( conn, "HTTPCONN %p closed %s://%s\n",
  89. conn, conn->scheme->name, conn->uri->host );
  90. } else {
  91. DBGC ( conn, "HTTPCONN %p closed %s://%s: %s\n",
  92. conn, conn->scheme->name, conn->uri->host,
  93. strerror ( rc ) );
  94. }
  95. }
  96. /**
  97. * Disconnect idle HTTP connection
  98. *
  99. * @v pool Pooled connection
  100. */
  101. static void http_conn_expired ( struct pooled_connection *pool ) {
  102. struct http_connection *conn =
  103. container_of ( pool, struct http_connection, pool );
  104. /* Close connection */
  105. http_conn_close ( conn, 0 /* Not an error to close idle connection */ );
  106. }
  107. /**
  108. * Receive data from transport layer interface
  109. *
  110. * @v http HTTP connection
  111. * @v iobuf I/O buffer
  112. * @v meta Transfer metadata
  113. * @ret rc Return status code
  114. */
  115. static int http_conn_socket_deliver ( struct http_connection *conn,
  116. struct io_buffer *iobuf,
  117. struct xfer_metadata *meta ) {
  118. /* Mark connection as alive */
  119. pool_alive ( &conn->pool );
  120. /* Pass on to data transfer interface */
  121. return xfer_deliver ( &conn->xfer, iobuf, meta );
  122. }
  123. /**
  124. * Close HTTP connection transport layer interface
  125. *
  126. * @v http HTTP connection
  127. * @v rc Reason for close
  128. */
  129. static void http_conn_socket_close ( struct http_connection *conn, int rc ) {
  130. /* If we are reopenable (i.e. we are a recycled connection
  131. * from the connection pool, and we have received no data from
  132. * the underlying socket since we were pooled), then suggest
  133. * that the client should reopen the connection.
  134. */
  135. if ( pool_is_reopenable ( &conn->pool ) )
  136. pool_reopen ( &conn->xfer );
  137. /* Close the connection */
  138. http_conn_close ( conn, rc );
  139. }
  140. /**
  141. * Recycle this connection after closing
  142. *
  143. * @v http HTTP connection
  144. */
  145. static void http_conn_xfer_recycle ( struct http_connection *conn ) {
  146. /* Mark connection as recyclable */
  147. pool_recyclable ( &conn->pool );
  148. DBGC2 ( conn, "HTTPCONN %p keepalive enabled\n", conn );
  149. }
  150. /**
  151. * Close HTTP connection data transfer interface
  152. *
  153. * @v conn HTTP connection
  154. * @v rc Reason for close
  155. */
  156. static void http_conn_xfer_close ( struct http_connection *conn, int rc ) {
  157. /* Add to the connection pool if keepalive is enabled and no
  158. * error occurred.
  159. */
  160. if ( ( rc == 0 ) && pool_is_recyclable ( &conn->pool ) ) {
  161. intf_restart ( &conn->xfer, rc );
  162. pool_add ( &conn->pool, &http_connection_pool,
  163. HTTP_CONN_EXPIRY );
  164. DBGC2 ( conn, "HTTPCONN %p pooled %s://%s\n",
  165. conn, conn->scheme->name, conn->uri->host );
  166. return;
  167. }
  168. /* Otherwise, close the connection */
  169. http_conn_close ( conn, rc );
  170. }
  171. /** HTTP connection socket interface operations */
  172. static struct interface_operation http_conn_socket_operations[] = {
  173. INTF_OP ( xfer_deliver, struct http_connection *,
  174. http_conn_socket_deliver ),
  175. INTF_OP ( intf_close, struct http_connection *,
  176. http_conn_socket_close ),
  177. };
  178. /** HTTP connection socket interface descriptor */
  179. static struct interface_descriptor http_conn_socket_desc =
  180. INTF_DESC_PASSTHRU ( struct http_connection, socket,
  181. http_conn_socket_operations, xfer );
  182. /** HTTP connection data transfer interface operations */
  183. static struct interface_operation http_conn_xfer_operations[] = {
  184. INTF_OP ( pool_recycle, struct http_connection *,
  185. http_conn_xfer_recycle ),
  186. INTF_OP ( intf_close, struct http_connection *,
  187. http_conn_xfer_close ),
  188. };
  189. /** HTTP connection data transfer interface descriptor */
  190. static struct interface_descriptor http_conn_xfer_desc =
  191. INTF_DESC_PASSTHRU ( struct http_connection, xfer,
  192. http_conn_xfer_operations, socket );
  193. /**
  194. * Connect to an HTTP server
  195. *
  196. * @v xfer Data transfer interface
  197. * @v uri Connection URI
  198. * @ret rc Return status code
  199. *
  200. * HTTP connections are pooled. The caller should be prepared to
  201. * receive a pool_reopen() message.
  202. */
  203. int http_connect ( struct interface *xfer, struct uri *uri ) {
  204. struct http_connection *conn;
  205. struct http_scheme *scheme;
  206. struct sockaddr_tcpip server;
  207. struct interface *socket;
  208. unsigned int port;
  209. int rc;
  210. /* Identify scheme */
  211. scheme = http_scheme ( uri );
  212. if ( ! scheme )
  213. return -ENOTSUP;
  214. /* Sanity check */
  215. if ( ! uri->host )
  216. return -EINVAL;
  217. /* Identify port */
  218. port = uri_port ( uri, scheme->port );
  219. /* Look for a reusable connection in the pool. Reuse the most
  220. * recent connection in order to accommodate authentication
  221. * schemes that break the stateless nature of HTTP and rely on
  222. * the same connection being reused for authentication
  223. * responses.
  224. */
  225. list_for_each_entry_reverse ( conn, &http_connection_pool, pool.list ) {
  226. /* Sanity checks */
  227. assert ( conn->uri != NULL );
  228. assert ( conn->uri->host != NULL );
  229. /* Reuse connection, if possible */
  230. if ( ( scheme == conn->scheme ) &&
  231. ( strcmp ( uri->host, conn->uri->host ) == 0 ) &&
  232. ( port == uri_port ( conn->uri, scheme->port ) ) ) {
  233. /* Remove from connection pool, stop timer,
  234. * attach to parent interface, and return.
  235. */
  236. pool_del ( &conn->pool );
  237. intf_plug_plug ( &conn->xfer, xfer );
  238. DBGC2 ( conn, "HTTPCONN %p reused %s://%s:%d\n", conn,
  239. conn->scheme->name, conn->uri->host, port );
  240. return 0;
  241. }
  242. }
  243. /* Allocate and initialise structure */
  244. conn = zalloc ( sizeof ( *conn ) );
  245. if ( ! conn ) {
  246. rc = -ENOMEM;
  247. goto err_alloc;
  248. }
  249. ref_init ( &conn->refcnt, http_conn_free );
  250. conn->uri = uri_get ( uri );
  251. conn->scheme = scheme;
  252. intf_init ( &conn->socket, &http_conn_socket_desc, &conn->refcnt );
  253. intf_init ( &conn->xfer, &http_conn_xfer_desc, &conn->refcnt );
  254. pool_init ( &conn->pool, http_conn_expired, &conn->refcnt );
  255. /* Open socket */
  256. memset ( &server, 0, sizeof ( server ) );
  257. server.st_port = htons ( port );
  258. socket = &conn->socket;
  259. if ( scheme->filter &&
  260. ( ( rc = scheme->filter ( socket, uri->host, &socket ) ) != 0 ) )
  261. goto err_filter;
  262. if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM,
  263. ( struct sockaddr * ) &server,
  264. uri->host, NULL ) ) != 0 )
  265. goto err_open;
  266. /* Attach to parent interface, mortalise self, and return */
  267. intf_plug_plug ( &conn->xfer, xfer );
  268. ref_put ( &conn->refcnt );
  269. DBGC2 ( conn, "HTTPCONN %p created %s://%s:%d\n", conn,
  270. conn->scheme->name, conn->uri->host, port );
  271. return 0;
  272. err_open:
  273. err_filter:
  274. DBGC2 ( conn, "HTTPCONN %p could not create %s://%s:%d: %s\n", conn,
  275. conn->scheme->name, conn->uri->host, port, strerror ( rc ) );
  276. http_conn_close ( conn, rc );
  277. ref_put ( &conn->refcnt );
  278. err_alloc:
  279. return rc;
  280. }