Browse Source

[http] Allow for HTTPS-only builds

Separate out the core HTTP functionality (which is shared by both HTTP
and HTTPS) from the provision of the "http://" URI opener.  This
allows for builds that support only "https://" URIs.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 12 years ago
parent
commit
99de239867
3 changed files with 889 additions and 859 deletions
  1. 1
    1
      src/include/ipxe/errfile.h
  2. 2
    858
      src/net/tcp/http.c
  3. 886
    0
      src/net/tcp/httpcore.c

+ 1
- 1
src/include/ipxe/errfile.h View File

@@ -161,7 +161,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
161 161
 #define ERRFILE_nullnet			( ERRFILE_NET | 0x00090000 )
162 162
 #define ERRFILE_tcp			( ERRFILE_NET | 0x000a0000 )
163 163
 #define ERRFILE_ftp			( ERRFILE_NET | 0x000b0000 )
164
-#define ERRFILE_http			( ERRFILE_NET | 0x000c0000 )
164
+#define ERRFILE_httpcore		( ERRFILE_NET | 0x000c0000 )
165 165
 #define ERRFILE_iscsi			( ERRFILE_NET | 0x000d0000 )
166 166
 #define ERRFILE_tcpip			( ERRFILE_NET | 0x000e0000 )
167 167
 #define ERRFILE_udp			( ERRFILE_NET | 0x000f0000 )

+ 2
- 858
src/net/tcp/http.c View File

@@ -25,869 +25,13 @@ FILE_LICENCE ( GPL2_OR_LATER );
25 25
  *
26 26
  */
27 27
 
28
-#include <stdint.h>
29
-#include <stdlib.h>
30
-#include <stdio.h>
31
-#include <string.h>
32
-#include <strings.h>
33
-#include <byteswap.h>
34
-#include <errno.h>
35
-#include <assert.h>
36
-#include <ipxe/uri.h>
37
-#include <ipxe/refcnt.h>
38
-#include <ipxe/iobuf.h>
39
-#include <ipxe/xfer.h>
28
+#include <stddef.h>
40 29
 #include <ipxe/open.h>
41
-#include <ipxe/socket.h>
42
-#include <ipxe/tcpip.h>
43
-#include <ipxe/process.h>
44
-#include <ipxe/linebuf.h>
45
-#include <ipxe/features.h>
46
-#include <ipxe/base64.h>
47
-#include <ipxe/blockdev.h>
48
-#include <ipxe/acpi.h>
49 30
 #include <ipxe/http.h>
31
+#include <ipxe/features.h>
50 32
 
51 33
 FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 );
52 34
 
53
-/** Block size used for HTTP block device request */
54
-#define HTTP_BLKSIZE 512
55
-
56
-/** HTTP flags */
57
-enum http_flags {
58
-	/** Request is waiting to be transmitted */
59
-	HTTP_TX_PENDING = 0x0001,
60
-	/** Fetch header only */
61
-	HTTP_HEAD_ONLY = 0x0002,
62
-	/** Keep connection alive */
63
-	HTTP_KEEPALIVE = 0x0004,
64
-};
65
-
66
-/** HTTP receive state */
67
-enum http_rx_state {
68
-	HTTP_RX_RESPONSE = 0,
69
-	HTTP_RX_HEADER,
70
-	HTTP_RX_CHUNK_LEN,
71
-	HTTP_RX_DATA,
72
-	HTTP_RX_TRAILER,
73
-	HTTP_RX_IDLE,
74
-	HTTP_RX_DEAD,
75
-};
76
-
77
-/**
78
- * An HTTP request
79
- *
80
- */
81
-struct http_request {
82
-	/** Reference count */
83
-	struct refcnt refcnt;
84
-	/** Data transfer interface */
85
-	struct interface xfer;
86
-	/** Partial transfer interface */
87
-	struct interface partial;
88
-
89
-	/** URI being fetched */
90
-	struct uri *uri;
91
-	/** Transport layer interface */
92
-	struct interface socket;
93
-
94
-	/** Flags */
95
-	unsigned int flags;
96
-	/** Starting offset of partial transfer (if applicable) */
97
-	size_t partial_start;
98
-	/** Length of partial transfer (if applicable) */
99
-	size_t partial_len;
100
-
101
-	/** TX process */
102
-	struct process process;
103
-
104
-	/** RX state */
105
-	enum http_rx_state rx_state;
106
-	/** Received length */
107
-	size_t rx_len;
108
-	/** Length remaining (or 0 if unknown) */
109
-	size_t remaining;
110
-	/** HTTP is using Transfer-Encoding: chunked */
111
-	int chunked;
112
-	/** Current chunk length remaining (if applicable) */
113
-	size_t chunk_remaining;
114
-	/** Line buffer for received header lines */
115
-	struct line_buffer linebuf;
116
-	/** Receive data buffer (if applicable) */
117
-	userptr_t rx_buffer;
118
-};
119
-
120
-/**
121
- * Free HTTP request
122
- *
123
- * @v refcnt		Reference counter
124
- */
125
-static void http_free ( struct refcnt *refcnt ) {
126
-	struct http_request *http =
127
-		container_of ( refcnt, struct http_request, refcnt );
128
-
129
-	uri_put ( http->uri );
130
-	empty_line_buffer ( &http->linebuf );
131
-	free ( http );
132
-};
133
-
134
-/**
135
- * Close HTTP request
136
- *
137
- * @v http		HTTP request
138
- * @v rc		Return status code
139
- */
140
-static void http_close ( struct http_request *http, int rc ) {
141
-
142
-	/* Prevent further processing of any current packet */
143
-	http->rx_state = HTTP_RX_DEAD;
144
-
145
-	/* If we had a Content-Length, and the received content length
146
-	 * isn't correct, flag an error
147
-	 */
148
-	if ( http->remaining != 0 ) {
149
-		DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n",
150
-		       http, http->rx_len, ( http->rx_len + http->remaining ) );
151
-		if ( rc == 0 )
152
-			rc = -EIO;
153
-	}
154
-
155
-	/* Remove process */
156
-	process_del ( &http->process );
157
-
158
-	/* Close all data transfer interfaces */
159
-	intf_shutdown ( &http->socket, rc );
160
-	intf_shutdown ( &http->partial, rc );
161
-	intf_shutdown ( &http->xfer, rc );
162
-}
163
-
164
-/**
165
- * Mark HTTP request as completed successfully
166
- *
167
- * @v http		HTTP request
168
- */
169
-static void http_done ( struct http_request *http ) {
170
-
171
-	/* If we had a Content-Length, and the received content length
172
-	 * isn't correct, force an error
173
-	 */
174
-	if ( http->remaining != 0 ) {
175
-		http_close ( http, -EIO );
176
-		return;
177
-	}
178
-
179
-	/* Enter idle state */
180
-	http->rx_state = HTTP_RX_IDLE;
181
-	http->rx_len = 0;
182
-	assert ( http->remaining == 0 );
183
-	assert ( http->chunked == 0 );
184
-	assert ( http->chunk_remaining == 0 );
185
-
186
-	/* Close partial transfer interface */
187
-	intf_restart ( &http->partial, 0 );
188
-
189
-	/* Close everything unless we are keeping the connection alive */
190
-	if ( ! ( http->flags & HTTP_KEEPALIVE ) )
191
-		http_close ( http, 0 );
192
-}
193
-
194
-/**
195
- * Convert HTTP response code to return status code
196
- *
197
- * @v response		HTTP response code
198
- * @ret rc		Return status code
199
- */
200
-static int http_response_to_rc ( unsigned int response ) {
201
-	switch ( response ) {
202
-	case 200:
203
-	case 206:
204
-	case 301:
205
-	case 302:
206
-		return 0;
207
-	case 404:
208
-		return -ENOENT;
209
-	case 403:
210
-		return -EPERM;
211
-	case 401:
212
-		return -EACCES;
213
-	default:
214
-		return -EIO;
215
-	}
216
-}
217
-
218
-/**
219
- * Handle HTTP response
220
- *
221
- * @v http		HTTP request
222
- * @v response		HTTP response
223
- * @ret rc		Return status code
224
- */
225
-static int http_rx_response ( struct http_request *http, char *response ) {
226
-	char *spc;
227
-	unsigned int code;
228
-	int rc;
229
-
230
-	DBGC ( http, "HTTP %p response \"%s\"\n", http, response );
231
-
232
-	/* Check response starts with "HTTP/" */
233
-	if ( strncmp ( response, "HTTP/", 5 ) != 0 )
234
-		return -EIO;
235
-
236
-	/* Locate and check response code */
237
-	spc = strchr ( response, ' ' );
238
-	if ( ! spc )
239
-		return -EIO;
240
-	code = strtoul ( spc, NULL, 10 );
241
-	if ( ( rc = http_response_to_rc ( code ) ) != 0 )
242
-		return rc;
243
-
244
-	/* Move to received headers */
245
-	http->rx_state = HTTP_RX_HEADER;
246
-	return 0;
247
-}
248
-
249
-/**
250
- * Handle HTTP Location header
251
- *
252
- * @v http		HTTP request
253
- * @v value		HTTP header value
254
- * @ret rc		Return status code
255
- */
256
-static int http_rx_location ( struct http_request *http, const char *value ) {
257
-	int rc;
258
-
259
-	/* Redirect to new location */
260
-	DBGC ( http, "HTTP %p redirecting to %s\n", http, value );
261
-	if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING,
262
-				    value ) ) != 0 ) {
263
-		DBGC ( http, "HTTP %p could not redirect: %s\n",
264
-		       http, strerror ( rc ) );
265
-		return rc;
266
-	}
267
-
268
-	return 0;
269
-}
270
-
271
-/**
272
- * Handle HTTP Content-Length header
273
- *
274
- * @v http		HTTP request
275
- * @v value		HTTP header value
276
- * @ret rc		Return status code
277
- */
278
-static int http_rx_content_length ( struct http_request *http,
279
-				    const char *value ) {
280
-	struct block_device_capacity capacity;
281
-	size_t content_len;
282
-	char *endp;
283
-
284
-	/* Parse content length */
285
-	content_len = strtoul ( value, &endp, 10 );
286
-	if ( *endp != '\0' ) {
287
-		DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n",
288
-		       http, value );
289
-		return -EIO;
290
-	}
291
-
292
-	/* If we already have an expected content length, and this
293
-	 * isn't it, then complain
294
-	 */
295
-	if ( http->remaining && ( http->remaining != content_len ) ) {
296
-		DBGC ( http, "HTTP %p incorrect Content-Length %zd (expected "
297
-		       "%zd)\n", http, content_len, http->remaining );
298
-		return -EIO;
299
-	}
300
-	if ( ! ( http->flags & HTTP_HEAD_ONLY ) )
301
-		http->remaining = content_len;
302
-
303
-	/* Use seek() to notify recipient of filesize */
304
-	xfer_seek ( &http->xfer, http->remaining );
305
-	xfer_seek ( &http->xfer, 0 );
306
-
307
-	/* Report block device capacity if applicable */
308
-	if ( http->flags & HTTP_HEAD_ONLY ) {
309
-		capacity.blocks = ( content_len / HTTP_BLKSIZE );
310
-		capacity.blksize = HTTP_BLKSIZE;
311
-		capacity.max_count = -1U;
312
-		block_capacity ( &http->partial, &capacity );
313
-	}
314
-	return 0;
315
-}
316
-
317
-/**
318
- * Handle HTTP Transfer-Encoding header
319
- *
320
- * @v http		HTTP request
321
- * @v value		HTTP header value
322
- * @ret rc		Return status code
323
- */
324
-static int http_rx_transfer_encoding ( struct http_request *http,
325
-				       const char *value ) {
326
-
327
-	if ( strcmp ( value, "chunked" ) == 0 ) {
328
-		/* Mark connection as using chunked transfer encoding */
329
-		http->chunked = 1;
330
-	}
331
-
332
-	return 0;
333
-}
334
-
335
-/** An HTTP header handler */
336
-struct http_header_handler {
337
-	/** Name (e.g. "Content-Length") */
338
-	const char *header;
339
-	/** Handle received header
340
-	 *
341
-	 * @v http	HTTP request
342
-	 * @v value	HTTP header value
343
-	 * @ret rc	Return status code
344
-	 *
345
-	 * If an error is returned, the download will be aborted.
346
-	 */
347
-	int ( * rx ) ( struct http_request *http, const char *value );
348
-};
349
-
350
-/** List of HTTP header handlers */
351
-static struct http_header_handler http_header_handlers[] = {
352
-	{
353
-		.header = "Location",
354
-		.rx = http_rx_location,
355
-	},
356
-	{
357
-		.header = "Content-Length",
358
-		.rx = http_rx_content_length,
359
-	},
360
-	{
361
-		.header = "Transfer-Encoding",
362
-		.rx = http_rx_transfer_encoding,
363
-	},
364
-	{ NULL, NULL }
365
-};
366
-
367
-/**
368
- * Handle HTTP header
369
- *
370
- * @v http		HTTP request
371
- * @v header		HTTP header
372
- * @ret rc		Return status code
373
- */
374
-static int http_rx_header ( struct http_request *http, char *header ) {
375
-	struct http_header_handler *handler;
376
-	char *separator;
377
-	char *value;
378
-	int rc;
379
-
380
-	/* An empty header line marks the end of this phase */
381
-	if ( ! header[0] ) {
382
-		empty_line_buffer ( &http->linebuf );
383
-		if ( ( http->rx_state == HTTP_RX_HEADER ) &&
384
-		     ( ! ( http->flags & HTTP_HEAD_ONLY ) ) ) {
385
-			DBGC ( http, "HTTP %p start of data\n", http );
386
-			http->rx_state = ( http->chunked ?
387
-					   HTTP_RX_CHUNK_LEN : HTTP_RX_DATA );
388
-			return 0;
389
-		} else {
390
-			DBGC ( http, "HTTP %p end of trailer\n", http );
391
-			http_done ( http );
392
-			return 0;
393
-		}
394
-	}
395
-
396
-	DBGC ( http, "HTTP %p header \"%s\"\n", http, header );
397
-
398
-	/* Split header at the ": " */
399
-	separator = strstr ( header, ": " );
400
-	if ( ! separator ) {
401
-		DBGC ( http, "HTTP %p malformed header\n", http );
402
-		return -EIO;
403
-	}
404
-	*separator = '\0';
405
-	value = ( separator + 2 );
406
-
407
-	/* Hand off to header handler, if one exists */
408
-	for ( handler = http_header_handlers ; handler->header ; handler++ ) {
409
-		if ( strcasecmp ( header, handler->header ) == 0 ) {
410
-			if ( ( rc = handler->rx ( http, value ) ) != 0 )
411
-				return rc;
412
-			break;
413
-		}
414
-	}
415
-	return 0;
416
-}
417
-
418
-/**
419
- * Handle HTTP chunk length
420
- *
421
- * @v http		HTTP request
422
- * @v length		HTTP chunk length
423
- * @ret rc		Return status code
424
- */
425
-static int http_rx_chunk_len ( struct http_request *http, char *length ) {
426
-	char *endp;
427
-
428
-	/* Skip blank lines between chunks */
429
-	if ( length[0] == '\0' )
430
-		return 0;
431
-
432
-	/* Parse chunk length */
433
-	http->chunk_remaining = strtoul ( length, &endp, 16 );
434
-	if ( *endp != '\0' ) {
435
-		DBGC ( http, "HTTP %p invalid chunk length \"%s\"\n",
436
-		       http, length );
437
-		return -EIO;
438
-	}
439
-
440
-	/* Terminate chunked encoding if applicable */
441
-	if ( http->chunk_remaining == 0 ) {
442
-		DBGC ( http, "HTTP %p end of chunks\n", http );
443
-		http->chunked = 0;
444
-		http->rx_state = HTTP_RX_TRAILER;
445
-		return 0;
446
-	}
447
-
448
-	/* Use seek() to notify recipient of new filesize */
449
-	DBGC ( http, "HTTP %p start of chunk of length %zd\n",
450
-	       http, http->chunk_remaining );
451
-	xfer_seek ( &http->xfer, ( http->rx_len + http->chunk_remaining ) );
452
-	xfer_seek ( &http->xfer, http->rx_len );
453
-
454
-	/* Start receiving data */
455
-	http->rx_state = HTTP_RX_DATA;
456
-
457
-	return 0;
458
-}
459
-
460
-/** An HTTP line-based data handler */
461
-struct http_line_handler {
462
-	/** Handle line
463
-	 *
464
-	 * @v http	HTTP request
465
-	 * @v line	Line to handle
466
-	 * @ret rc	Return status code
467
-	 */
468
-	int ( * rx ) ( struct http_request *http, char *line );
469
-};
470
-
471
-/** List of HTTP line-based data handlers */
472
-static struct http_line_handler http_line_handlers[] = {
473
-	[HTTP_RX_RESPONSE]	= { .rx = http_rx_response },
474
-	[HTTP_RX_HEADER]	= { .rx = http_rx_header },
475
-	[HTTP_RX_CHUNK_LEN]	= { .rx = http_rx_chunk_len },
476
-	[HTTP_RX_TRAILER]	= { .rx = http_rx_header },
477
-};
478
-
479
-/**
480
- * Handle new data arriving via HTTP connection
481
- *
482
- * @v http		HTTP request
483
- * @v iobuf		I/O buffer
484
- * @v meta		Data transfer metadata
485
- * @ret rc		Return status code
486
- */
487
-static int http_socket_deliver ( struct http_request *http,
488
-				 struct io_buffer *iobuf,
489
-				 struct xfer_metadata *meta __unused ) {
490
-	struct http_line_handler *lh;
491
-	char *line;
492
-	size_t data_len;
493
-	ssize_t line_len;
494
-	int rc = 0;
495
-
496
-	while ( iobuf && iob_len ( iobuf ) ) {
497
-
498
-		switch ( http->rx_state ) {
499
-		case HTTP_RX_IDLE:
500
-			/* Receiving any data in this state is an error */
501
-			DBGC ( http, "HTTP %p received %zd bytes while %s\n",
502
-			       http, iob_len ( iobuf ),
503
-			       ( ( http->rx_state == HTTP_RX_IDLE ) ?
504
-				 "idle" : "dead" ) );
505
-			rc = -EPROTO;
506
-			goto done;
507
-		case HTTP_RX_DEAD:
508
-			/* Do no further processing */
509
-			goto done;
510
-		case HTTP_RX_DATA:
511
-			/* Pass received data to caller */
512
-			data_len = iob_len ( iobuf );
513
-			if ( http->chunk_remaining &&
514
-			     ( http->chunk_remaining < data_len ) ) {
515
-				data_len = http->chunk_remaining;
516
-			}
517
-			if ( http->remaining &&
518
-			     ( http->remaining < data_len ) ) {
519
-				data_len = http->remaining;
520
-			}
521
-			if ( http->rx_buffer != UNULL ) {
522
-				/* Copy to partial transfer buffer */
523
-				copy_to_user ( http->rx_buffer, http->rx_len,
524
-					       iobuf->data, data_len );
525
-				iob_pull ( iobuf, data_len );
526
-			} else if ( data_len < iob_len ( iobuf ) ) {
527
-				/* Deliver partial buffer as raw data */
528
-				rc = xfer_deliver_raw ( &http->xfer,
529
-							iobuf->data, data_len );
530
-				iob_pull ( iobuf, data_len );
531
-				if ( rc != 0 )
532
-					goto done;
533
-			} else {
534
-				/* Deliver whole I/O buffer */
535
-				if ( ( rc = xfer_deliver_iob ( &http->xfer,
536
-						 iob_disown ( iobuf ) ) ) != 0 )
537
-					goto done;
538
-			}
539
-			http->rx_len += data_len;
540
-			if ( http->chunk_remaining ) {
541
-				http->chunk_remaining -= data_len;
542
-				if ( http->chunk_remaining == 0 )
543
-					http->rx_state = HTTP_RX_CHUNK_LEN;
544
-			}
545
-			if ( http->remaining ) {
546
-				http->remaining -= data_len;
547
-				if ( ( http->remaining == 0 ) &&
548
-				     ( http->rx_state == HTTP_RX_DATA ) ) {
549
-					http_done ( http );
550
-				}
551
-			}
552
-			break;
553
-		case HTTP_RX_RESPONSE:
554
-		case HTTP_RX_HEADER:
555
-		case HTTP_RX_CHUNK_LEN:
556
-		case HTTP_RX_TRAILER:
557
-			/* In the other phases, buffer and process a
558
-			 * line at a time
559
-			 */
560
-			line_len = line_buffer ( &http->linebuf, iobuf->data,
561
-						 iob_len ( iobuf ) );
562
-			if ( line_len < 0 ) {
563
-				rc = line_len;
564
-				DBGC ( http, "HTTP %p could not buffer line: "
565
-				       "%s\n", http, strerror ( rc ) );
566
-				goto done;
567
-			}
568
-			iob_pull ( iobuf, line_len );
569
-			line = buffered_line ( &http->linebuf );
570
-			if ( line ) {
571
-				lh = &http_line_handlers[http->rx_state];
572
-				if ( ( rc = lh->rx ( http, line ) ) != 0 )
573
-					goto done;
574
-			}
575
-			break;
576
-		default:
577
-			assert ( 0 );
578
-			break;
579
-		}
580
-	}
581
-
582
- done:
583
-	if ( rc )
584
-		http_close ( http, rc );
585
-	free_iob ( iobuf );
586
-	return rc;
587
-}
588
-
589
-/**
590
- * Check HTTP socket flow control window
591
- *
592
- * @v http		HTTP request
593
- * @ret len		Length of window
594
- */
595
-static size_t http_socket_window ( struct http_request *http __unused ) {
596
-
597
-	/* Window is always open.  This is to prevent TCP from
598
-	 * stalling if our parent window is not currently open.
599
-	 */
600
-	return ( ~( ( size_t ) 0 ) );
601
-}
602
-
603
-/**
604
- * HTTP process
605
- *
606
- * @v http		HTTP request
607
- */
608
-static void http_step ( struct http_request *http ) {
609
-	const char *host = http->uri->host;
610
-	const char *user = http->uri->user;
611
-	const char *password =
612
-		( http->uri->password ? http->uri->password : "" );
613
-	size_t user_pw_len = ( user ? ( strlen ( user ) + 1 /* ":" */ +
614
-					strlen ( password ) ) : 0 );
615
-	size_t user_pw_base64_len = base64_encoded_len ( user_pw_len );
616
-	uint8_t user_pw[ user_pw_len + 1 /* NUL */ ];
617
-	char user_pw_base64[ user_pw_base64_len + 1 /* NUL */ ];
618
-	int rc;
619
-	int request_len = unparse_uri ( NULL, 0, http->uri,
620
-					URI_PATH_BIT | URI_QUERY_BIT );
621
-	char request[ request_len + 1 /* NUL */ ];
622
-	char range[48]; /* Enough for two 64-bit integers in decimal */
623
-	int partial;
624
-
625
-	/* Do nothing if we have already transmitted the request */
626
-	if ( ! ( http->flags & HTTP_TX_PENDING ) )
627
-		return;
628
-
629
-	/* Do nothing until socket is ready */
630
-	if ( ! xfer_window ( &http->socket ) )
631
-		return;
632
-
633
-	/* Construct path?query request */
634
-	unparse_uri ( request, sizeof ( request ), http->uri,
635
-		      URI_PATH_BIT | URI_QUERY_BIT );
636
-
637
-	/* Construct authorisation, if applicable */
638
-	if ( user ) {
639
-		/* Make "user:password" string from decoded fields */
640
-		snprintf ( ( ( char * ) user_pw ), sizeof ( user_pw ),
641
-			   "%s:%s", user, password );
642
-
643
-		/* Base64-encode the "user:password" string */
644
-		base64_encode ( user_pw, user_pw_len, user_pw_base64 );
645
-	}
646
-
647
-	/* Force a HEAD request if we have nowhere to send any received data */
648
-	if ( ( xfer_window ( &http->xfer ) == 0 ) &&
649
-	     ( http->rx_buffer == UNULL ) ) {
650
-		http->flags |= ( HTTP_HEAD_ONLY | HTTP_KEEPALIVE );
651
-	}
652
-
653
-	/* Determine type of request */
654
-	partial = ( http->partial_len != 0 );
655
-	snprintf ( range, sizeof ( range ), "%zd-%zd", http->partial_start,
656
-		   ( http->partial_start + http->partial_len - 1 ) );
657
-
658
-	/* Mark request as transmitted */
659
-	http->flags &= ~HTTP_TX_PENDING;
660
-
661
-	/* Send GET request */
662
-	if ( ( rc = xfer_printf ( &http->socket,
663
-				  "%s %s%s HTTP/1.1\r\n"
664
-				  "User-Agent: iPXE/" VERSION "\r\n"
665
-				  "Host: %s%s%s\r\n"
666
-				  "%s%s%s%s%s%s%s"
667
-				  "\r\n",
668
-				  ( ( http->flags & HTTP_HEAD_ONLY ) ?
669
-				    "HEAD" : "GET" ),
670
-				  ( http->uri->path ? "" : "/" ),
671
-				  request, host,
672
-				  ( http->uri->port ?
673
-				    ":" : "" ),
674
-				  ( http->uri->port ?
675
-				    http->uri->port : "" ),
676
-				  ( ( http->flags & HTTP_KEEPALIVE ) ?
677
-				    "Connection: Keep-Alive\r\n" : "" ),
678
-				  ( partial ? "Range: bytes=" : "" ),
679
-				  ( partial ? range : "" ),
680
-				  ( partial ? "\r\n" : "" ),
681
-				  ( user ?
682
-				    "Authorization: Basic " : "" ),
683
-				  ( user ? user_pw_base64 : "" ),
684
-				  ( user ? "\r\n" : "" ) ) ) != 0 ) {
685
-		http_close ( http, rc );
686
-	}
687
-}
688
-
689
-/**
690
- * Check HTTP data transfer flow control window
691
- *
692
- * @v http		HTTP request
693
- * @ret len		Length of window
694
- */
695
-static size_t http_xfer_window ( struct http_request *http ) {
696
-
697
-	/* New block commands may be issued only when we are idle */
698
-	return ( ( http->rx_state == HTTP_RX_IDLE ) ? 1 : 0 );
699
-}
700
-
701
-/**
702
- * Initiate HTTP partial read
703
- *
704
- * @v http		HTTP request
705
- * @v partial		Partial transfer interface
706
- * @v offset		Starting offset
707
- * @v buffer		Data buffer
708
- * @v len		Length
709
- * @ret rc		Return status code
710
- */
711
-static int http_partial_read ( struct http_request *http,
712
-			       struct interface *partial,
713
-			       size_t offset, userptr_t buffer, size_t len ) {
714
-
715
-	/* Sanity check */
716
-	if ( http_xfer_window ( http ) == 0 )
717
-		return -EBUSY;
718
-
719
-	/* Initialise partial transfer parameters */
720
-	http->rx_buffer = buffer;
721
-	http->partial_start = offset;
722
-	http->partial_len = len;
723
-	http->remaining = len;
724
-
725
-	/* Schedule request */
726
-	http->rx_state = HTTP_RX_RESPONSE;
727
-	http->flags = ( HTTP_TX_PENDING | HTTP_KEEPALIVE );
728
-	if ( ! len )
729
-		http->flags |= HTTP_HEAD_ONLY;
730
-	process_add ( &http->process );
731
-
732
-	/* Attach to parent interface and return */
733
-	intf_plug_plug ( &http->partial, partial );
734
-
735
-	return 0;
736
-}
737
-
738
-/**
739
- * Issue HTTP block device read
740
- *
741
- * @v http		HTTP request
742
- * @v block		Block data interface
743
- * @v lba		Starting logical block address
744
- * @v count		Number of blocks to transfer
745
- * @v buffer		Data buffer
746
- * @v len		Length of data buffer
747
- * @ret rc		Return status code
748
- */
749
-static int http_block_read ( struct http_request *http,
750
-			     struct interface *block,
751
-			     uint64_t lba, unsigned int count,
752
-			     userptr_t buffer, size_t len __unused ) {
753
-
754
-	return http_partial_read ( http, block, ( lba * HTTP_BLKSIZE ),
755
-				   buffer, ( count * HTTP_BLKSIZE ) );
756
-}
757
-
758
-/**
759
- * Read HTTP block device capacity
760
- *
761
- * @v http		HTTP request
762
- * @v block		Block data interface
763
- * @ret rc		Return status code
764
- */
765
-static int http_block_read_capacity ( struct http_request *http,
766
-				      struct interface *block ) {
767
-
768
-	return http_partial_read ( http, block, 0, 0, 0 );
769
-}
770
-
771
-/**
772
- * Describe HTTP device in an ACPI table
773
- *
774
- * @v http		HTTP request
775
- * @v acpi		ACPI table
776
- * @v len		Length of ACPI table
777
- * @ret rc		Return status code
778
- */
779
-static int http_acpi_describe ( struct http_request *http,
780
-				struct acpi_description_header *acpi,
781
-				size_t len ) {
782
-
783
-	DBGC ( http, "HTTP %p cannot yet describe device in an ACPI table\n",
784
-	       http );
785
-	( void ) acpi;
786
-	( void ) len;
787
-	return 0;
788
-}
789
-
790
-/** HTTP socket interface operations */
791
-static struct interface_operation http_socket_operations[] = {
792
-	INTF_OP ( xfer_window, struct http_request *, http_socket_window ),
793
-	INTF_OP ( xfer_deliver, struct http_request *, http_socket_deliver ),
794
-	INTF_OP ( xfer_window_changed, struct http_request *, http_step ),
795
-	INTF_OP ( intf_close, struct http_request *, http_close ),
796
-};
797
-
798
-/** HTTP socket interface descriptor */
799
-static struct interface_descriptor http_socket_desc =
800
-	INTF_DESC_PASSTHRU ( struct http_request, socket,
801
-			     http_socket_operations, xfer );
802
-
803
-/** HTTP partial transfer interface operations */
804
-static struct interface_operation http_partial_operations[] = {
805
-	INTF_OP ( intf_close, struct http_request *, http_close ),
806
-};
807
-
808
-/** HTTP partial transfer interface descriptor */
809
-static struct interface_descriptor http_partial_desc =
810
-	INTF_DESC ( struct http_request, partial, http_partial_operations );
811
-
812
-/** HTTP data transfer interface operations */
813
-static struct interface_operation http_xfer_operations[] = {
814
-	INTF_OP ( xfer_window, struct http_request *, http_xfer_window ),
815
-	INTF_OP ( block_read, struct http_request *, http_block_read ),
816
-	INTF_OP ( block_read_capacity, struct http_request *,
817
-		  http_block_read_capacity ),
818
-	INTF_OP ( intf_close, struct http_request *, http_close ),
819
-	INTF_OP ( acpi_describe, struct http_request *, http_acpi_describe ),
820
-};
821
-
822
-/** HTTP data transfer interface descriptor */
823
-static struct interface_descriptor http_xfer_desc =
824
-	INTF_DESC_PASSTHRU ( struct http_request, xfer,
825
-			     http_xfer_operations, socket );
826
-
827
-/** HTTP process descriptor */
828
-static struct process_descriptor http_process_desc =
829
-	PROC_DESC_ONCE ( struct http_request, process, http_step );
830
-
831
-/**
832
- * Initiate an HTTP connection, with optional filter
833
- *
834
- * @v xfer		Data transfer interface
835
- * @v uri		Uniform Resource Identifier
836
- * @v default_port	Default port number
837
- * @v filter		Filter to apply to socket, or NULL
838
- * @ret rc		Return status code
839
- */
840
-int http_open_filter ( struct interface *xfer, struct uri *uri,
841
-		       unsigned int default_port,
842
-		       int ( * filter ) ( struct interface *xfer,
843
-					  struct interface **next ) ) {
844
-	struct http_request *http;
845
-	struct sockaddr_tcpip server;
846
-	struct interface *socket;
847
-	int rc;
848
-
849
-	/* Sanity checks */
850
-	if ( ! uri->host )
851
-		return -EINVAL;
852
-
853
-	/* Allocate and populate HTTP structure */
854
-	http = zalloc ( sizeof ( *http ) );
855
-	if ( ! http )
856
-		return -ENOMEM;
857
-	ref_init ( &http->refcnt, http_free );
858
-	intf_init ( &http->xfer, &http_xfer_desc, &http->refcnt );
859
-	intf_init ( &http->partial, &http_partial_desc, &http->refcnt );
860
-       	http->uri = uri_get ( uri );
861
-	intf_init ( &http->socket, &http_socket_desc, &http->refcnt );
862
-	process_init ( &http->process, &http_process_desc, &http->refcnt );
863
-	http->flags = HTTP_TX_PENDING;
864
-
865
-	/* Open socket */
866
-	memset ( &server, 0, sizeof ( server ) );
867
-	server.st_port = htons ( uri_port ( http->uri, default_port ) );
868
-	socket = &http->socket;
869
-	if ( filter ) {
870
-		if ( ( rc = filter ( socket, &socket ) ) != 0 )
871
-			goto err;
872
-	}
873
-	if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM,
874
-					     ( struct sockaddr * ) &server,
875
-					     uri->host, NULL ) ) != 0 )
876
-		goto err;
877
-
878
-	/* Attach to parent interface, mortalise self, and return */
879
-	intf_plug_plug ( &http->xfer, xfer );
880
-	ref_put ( &http->refcnt );
881
-	return 0;
882
-
883
- err:
884
-	DBGC ( http, "HTTP %p could not create request: %s\n", 
885
-	       http, strerror ( rc ) );
886
-	http_close ( http, rc );
887
-	ref_put ( &http->refcnt );
888
-	return rc;
889
-}
890
-
891 35
 /**
892 36
  * Initiate an HTTP connection
893 37
  *

+ 886
- 0
src/net/tcp/httpcore.c View File

@@ -0,0 +1,886 @@
1
+/*
2
+ * Copyright (C) 2007 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., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+FILE_LICENCE ( GPL2_OR_LATER );
20
+
21
+/**
22
+ * @file
23
+ *
24
+ * Hyper Text Transfer Protocol (HTTP) core functionality
25
+ *
26
+ */
27
+
28
+#include <stdint.h>
29
+#include <stdlib.h>
30
+#include <stdio.h>
31
+#include <string.h>
32
+#include <strings.h>
33
+#include <byteswap.h>
34
+#include <errno.h>
35
+#include <assert.h>
36
+#include <ipxe/uri.h>
37
+#include <ipxe/refcnt.h>
38
+#include <ipxe/iobuf.h>
39
+#include <ipxe/xfer.h>
40
+#include <ipxe/open.h>
41
+#include <ipxe/socket.h>
42
+#include <ipxe/tcpip.h>
43
+#include <ipxe/process.h>
44
+#include <ipxe/linebuf.h>
45
+#include <ipxe/base64.h>
46
+#include <ipxe/blockdev.h>
47
+#include <ipxe/acpi.h>
48
+#include <ipxe/http.h>
49
+
50
+/** Block size used for HTTP block device request */
51
+#define HTTP_BLKSIZE 512
52
+
53
+/** HTTP flags */
54
+enum http_flags {
55
+	/** Request is waiting to be transmitted */
56
+	HTTP_TX_PENDING = 0x0001,
57
+	/** Fetch header only */
58
+	HTTP_HEAD_ONLY = 0x0002,
59
+	/** Keep connection alive */
60
+	HTTP_KEEPALIVE = 0x0004,
61
+};
62
+
63
+/** HTTP receive state */
64
+enum http_rx_state {
65
+	HTTP_RX_RESPONSE = 0,
66
+	HTTP_RX_HEADER,
67
+	HTTP_RX_CHUNK_LEN,
68
+	HTTP_RX_DATA,
69
+	HTTP_RX_TRAILER,
70
+	HTTP_RX_IDLE,
71
+	HTTP_RX_DEAD,
72
+};
73
+
74
+/**
75
+ * An HTTP request
76
+ *
77
+ */
78
+struct http_request {
79
+	/** Reference count */
80
+	struct refcnt refcnt;
81
+	/** Data transfer interface */
82
+	struct interface xfer;
83
+	/** Partial transfer interface */
84
+	struct interface partial;
85
+
86
+	/** URI being fetched */
87
+	struct uri *uri;
88
+	/** Transport layer interface */
89
+	struct interface socket;
90
+
91
+	/** Flags */
92
+	unsigned int flags;
93
+	/** Starting offset of partial transfer (if applicable) */
94
+	size_t partial_start;
95
+	/** Length of partial transfer (if applicable) */
96
+	size_t partial_len;
97
+
98
+	/** TX process */
99
+	struct process process;
100
+
101
+	/** RX state */
102
+	enum http_rx_state rx_state;
103
+	/** Received length */
104
+	size_t rx_len;
105
+	/** Length remaining (or 0 if unknown) */
106
+	size_t remaining;
107
+	/** HTTP is using Transfer-Encoding: chunked */
108
+	int chunked;
109
+	/** Current chunk length remaining (if applicable) */
110
+	size_t chunk_remaining;
111
+	/** Line buffer for received header lines */
112
+	struct line_buffer linebuf;
113
+	/** Receive data buffer (if applicable) */
114
+	userptr_t rx_buffer;
115
+};
116
+
117
+/**
118
+ * Free HTTP request
119
+ *
120
+ * @v refcnt		Reference counter
121
+ */
122
+static void http_free ( struct refcnt *refcnt ) {
123
+	struct http_request *http =
124
+		container_of ( refcnt, struct http_request, refcnt );
125
+
126
+	uri_put ( http->uri );
127
+	empty_line_buffer ( &http->linebuf );
128
+	free ( http );
129
+};
130
+
131
+/**
132
+ * Close HTTP request
133
+ *
134
+ * @v http		HTTP request
135
+ * @v rc		Return status code
136
+ */
137
+static void http_close ( struct http_request *http, int rc ) {
138
+
139
+	/* Prevent further processing of any current packet */
140
+	http->rx_state = HTTP_RX_DEAD;
141
+
142
+	/* If we had a Content-Length, and the received content length
143
+	 * isn't correct, flag an error
144
+	 */
145
+	if ( http->remaining != 0 ) {
146
+		DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n",
147
+		       http, http->rx_len, ( http->rx_len + http->remaining ) );
148
+		if ( rc == 0 )
149
+			rc = -EIO;
150
+	}
151
+
152
+	/* Remove process */
153
+	process_del ( &http->process );
154
+
155
+	/* Close all data transfer interfaces */
156
+	intf_shutdown ( &http->socket, rc );
157
+	intf_shutdown ( &http->partial, rc );
158
+	intf_shutdown ( &http->xfer, rc );
159
+}
160
+
161
+/**
162
+ * Mark HTTP request as completed successfully
163
+ *
164
+ * @v http		HTTP request
165
+ */
166
+static void http_done ( struct http_request *http ) {
167
+
168
+	/* If we had a Content-Length, and the received content length
169
+	 * isn't correct, force an error
170
+	 */
171
+	if ( http->remaining != 0 ) {
172
+		http_close ( http, -EIO );
173
+		return;
174
+	}
175
+
176
+	/* Enter idle state */
177
+	http->rx_state = HTTP_RX_IDLE;
178
+	http->rx_len = 0;
179
+	assert ( http->remaining == 0 );
180
+	assert ( http->chunked == 0 );
181
+	assert ( http->chunk_remaining == 0 );
182
+
183
+	/* Close partial transfer interface */
184
+	intf_restart ( &http->partial, 0 );
185
+
186
+	/* Close everything unless we are keeping the connection alive */
187
+	if ( ! ( http->flags & HTTP_KEEPALIVE ) )
188
+		http_close ( http, 0 );
189
+}
190
+
191
+/**
192
+ * Convert HTTP response code to return status code
193
+ *
194
+ * @v response		HTTP response code
195
+ * @ret rc		Return status code
196
+ */
197
+static int http_response_to_rc ( unsigned int response ) {
198
+	switch ( response ) {
199
+	case 200:
200
+	case 206:
201
+	case 301:
202
+	case 302:
203
+		return 0;
204
+	case 404:
205
+		return -ENOENT;
206
+	case 403:
207
+		return -EPERM;
208
+	case 401:
209
+		return -EACCES;
210
+	default:
211
+		return -EIO;
212
+	}
213
+}
214
+
215
+/**
216
+ * Handle HTTP response
217
+ *
218
+ * @v http		HTTP request
219
+ * @v response		HTTP response
220
+ * @ret rc		Return status code
221
+ */
222
+static int http_rx_response ( struct http_request *http, char *response ) {
223
+	char *spc;
224
+	unsigned int code;
225
+	int rc;
226
+
227
+	DBGC ( http, "HTTP %p response \"%s\"\n", http, response );
228
+
229
+	/* Check response starts with "HTTP/" */
230
+	if ( strncmp ( response, "HTTP/", 5 ) != 0 )
231
+		return -EIO;
232
+
233
+	/* Locate and check response code */
234
+	spc = strchr ( response, ' ' );
235
+	if ( ! spc )
236
+		return -EIO;
237
+	code = strtoul ( spc, NULL, 10 );
238
+	if ( ( rc = http_response_to_rc ( code ) ) != 0 )
239
+		return rc;
240
+
241
+	/* Move to received headers */
242
+	http->rx_state = HTTP_RX_HEADER;
243
+	return 0;
244
+}
245
+
246
+/**
247
+ * Handle HTTP Location header
248
+ *
249
+ * @v http		HTTP request
250
+ * @v value		HTTP header value
251
+ * @ret rc		Return status code
252
+ */
253
+static int http_rx_location ( struct http_request *http, const char *value ) {
254
+	int rc;
255
+
256
+	/* Redirect to new location */
257
+	DBGC ( http, "HTTP %p redirecting to %s\n", http, value );
258
+	if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING,
259
+				    value ) ) != 0 ) {
260
+		DBGC ( http, "HTTP %p could not redirect: %s\n",
261
+		       http, strerror ( rc ) );
262
+		return rc;
263
+	}
264
+
265
+	return 0;
266
+}
267
+
268
+/**
269
+ * Handle HTTP Content-Length header
270
+ *
271
+ * @v http		HTTP request
272
+ * @v value		HTTP header value
273
+ * @ret rc		Return status code
274
+ */
275
+static int http_rx_content_length ( struct http_request *http,
276
+				    const char *value ) {
277
+	struct block_device_capacity capacity;
278
+	size_t content_len;
279
+	char *endp;
280
+
281
+	/* Parse content length */
282
+	content_len = strtoul ( value, &endp, 10 );
283
+	if ( *endp != '\0' ) {
284
+		DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n",
285
+		       http, value );
286
+		return -EIO;
287
+	}
288
+
289
+	/* If we already have an expected content length, and this
290
+	 * isn't it, then complain
291
+	 */
292
+	if ( http->remaining && ( http->remaining != content_len ) ) {
293
+		DBGC ( http, "HTTP %p incorrect Content-Length %zd (expected "
294
+		       "%zd)\n", http, content_len, http->remaining );
295
+		return -EIO;
296
+	}
297
+	if ( ! ( http->flags & HTTP_HEAD_ONLY ) )
298
+		http->remaining = content_len;
299
+
300
+	/* Use seek() to notify recipient of filesize */
301
+	xfer_seek ( &http->xfer, http->remaining );
302
+	xfer_seek ( &http->xfer, 0 );
303
+
304
+	/* Report block device capacity if applicable */
305
+	if ( http->flags & HTTP_HEAD_ONLY ) {
306
+		capacity.blocks = ( content_len / HTTP_BLKSIZE );
307
+		capacity.blksize = HTTP_BLKSIZE;
308
+		capacity.max_count = -1U;
309
+		block_capacity ( &http->partial, &capacity );
310
+	}
311
+	return 0;
312
+}
313
+
314
+/**
315
+ * Handle HTTP Transfer-Encoding header
316
+ *
317
+ * @v http		HTTP request
318
+ * @v value		HTTP header value
319
+ * @ret rc		Return status code
320
+ */
321
+static int http_rx_transfer_encoding ( struct http_request *http,
322
+				       const char *value ) {
323
+
324
+	if ( strcmp ( value, "chunked" ) == 0 ) {
325
+		/* Mark connection as using chunked transfer encoding */
326
+		http->chunked = 1;
327
+	}
328
+
329
+	return 0;
330
+}
331
+
332
+/** An HTTP header handler */
333
+struct http_header_handler {
334
+	/** Name (e.g. "Content-Length") */
335
+	const char *header;
336
+	/** Handle received header
337
+	 *
338
+	 * @v http	HTTP request
339
+	 * @v value	HTTP header value
340
+	 * @ret rc	Return status code
341
+	 *
342
+	 * If an error is returned, the download will be aborted.
343
+	 */
344
+	int ( * rx ) ( struct http_request *http, const char *value );
345
+};
346
+
347
+/** List of HTTP header handlers */
348
+static struct http_header_handler http_header_handlers[] = {
349
+	{
350
+		.header = "Location",
351
+		.rx = http_rx_location,
352
+	},
353
+	{
354
+		.header = "Content-Length",
355
+		.rx = http_rx_content_length,
356
+	},
357
+	{
358
+		.header = "Transfer-Encoding",
359
+		.rx = http_rx_transfer_encoding,
360
+	},
361
+	{ NULL, NULL }
362
+};
363
+
364
+/**
365
+ * Handle HTTP header
366
+ *
367
+ * @v http		HTTP request
368
+ * @v header		HTTP header
369
+ * @ret rc		Return status code
370
+ */
371
+static int http_rx_header ( struct http_request *http, char *header ) {
372
+	struct http_header_handler *handler;
373
+	char *separator;
374
+	char *value;
375
+	int rc;
376
+
377
+	/* An empty header line marks the end of this phase */
378
+	if ( ! header[0] ) {
379
+		empty_line_buffer ( &http->linebuf );
380
+		if ( ( http->rx_state == HTTP_RX_HEADER ) &&
381
+		     ( ! ( http->flags & HTTP_HEAD_ONLY ) ) ) {
382
+			DBGC ( http, "HTTP %p start of data\n", http );
383
+			http->rx_state = ( http->chunked ?
384
+					   HTTP_RX_CHUNK_LEN : HTTP_RX_DATA );
385
+			return 0;
386
+		} else {
387
+			DBGC ( http, "HTTP %p end of trailer\n", http );
388
+			http_done ( http );
389
+			return 0;
390
+		}
391
+	}
392
+
393
+	DBGC ( http, "HTTP %p header \"%s\"\n", http, header );
394
+
395
+	/* Split header at the ": " */
396
+	separator = strstr ( header, ": " );
397
+	if ( ! separator ) {
398
+		DBGC ( http, "HTTP %p malformed header\n", http );
399
+		return -EIO;
400
+	}
401
+	*separator = '\0';
402
+	value = ( separator + 2 );
403
+
404
+	/* Hand off to header handler, if one exists */
405
+	for ( handler = http_header_handlers ; handler->header ; handler++ ) {
406
+		if ( strcasecmp ( header, handler->header ) == 0 ) {
407
+			if ( ( rc = handler->rx ( http, value ) ) != 0 )
408
+				return rc;
409
+			break;
410
+		}
411
+	}
412
+	return 0;
413
+}
414
+
415
+/**
416
+ * Handle HTTP chunk length
417
+ *
418
+ * @v http		HTTP request
419
+ * @v length		HTTP chunk length
420
+ * @ret rc		Return status code
421
+ */
422
+static int http_rx_chunk_len ( struct http_request *http, char *length ) {
423
+	char *endp;
424
+
425
+	/* Skip blank lines between chunks */
426
+	if ( length[0] == '\0' )
427
+		return 0;
428
+
429
+	/* Parse chunk length */
430
+	http->chunk_remaining = strtoul ( length, &endp, 16 );
431
+	if ( *endp != '\0' ) {
432
+		DBGC ( http, "HTTP %p invalid chunk length \"%s\"\n",
433
+		       http, length );
434
+		return -EIO;
435
+	}
436
+
437
+	/* Terminate chunked encoding if applicable */
438
+	if ( http->chunk_remaining == 0 ) {
439
+		DBGC ( http, "HTTP %p end of chunks\n", http );
440
+		http->chunked = 0;
441
+		http->rx_state = HTTP_RX_TRAILER;
442
+		return 0;
443
+	}
444
+
445
+	/* Use seek() to notify recipient of new filesize */
446
+	DBGC ( http, "HTTP %p start of chunk of length %zd\n",
447
+	       http, http->chunk_remaining );
448
+	xfer_seek ( &http->xfer, ( http->rx_len + http->chunk_remaining ) );
449
+	xfer_seek ( &http->xfer, http->rx_len );
450
+
451
+	/* Start receiving data */
452
+	http->rx_state = HTTP_RX_DATA;
453
+
454
+	return 0;
455
+}
456
+
457
+/** An HTTP line-based data handler */
458
+struct http_line_handler {
459
+	/** Handle line
460
+	 *
461
+	 * @v http	HTTP request
462
+	 * @v line	Line to handle
463
+	 * @ret rc	Return status code
464
+	 */
465
+	int ( * rx ) ( struct http_request *http, char *line );
466
+};
467
+
468
+/** List of HTTP line-based data handlers */
469
+static struct http_line_handler http_line_handlers[] = {
470
+	[HTTP_RX_RESPONSE]	= { .rx = http_rx_response },
471
+	[HTTP_RX_HEADER]	= { .rx = http_rx_header },
472
+	[HTTP_RX_CHUNK_LEN]	= { .rx = http_rx_chunk_len },
473
+	[HTTP_RX_TRAILER]	= { .rx = http_rx_header },
474
+};
475
+
476
+/**
477
+ * Handle new data arriving via HTTP connection
478
+ *
479
+ * @v http		HTTP request
480
+ * @v iobuf		I/O buffer
481
+ * @v meta		Data transfer metadata
482
+ * @ret rc		Return status code
483
+ */
484
+static int http_socket_deliver ( struct http_request *http,
485
+				 struct io_buffer *iobuf,
486
+				 struct xfer_metadata *meta __unused ) {
487
+	struct http_line_handler *lh;
488
+	char *line;
489
+	size_t data_len;
490
+	ssize_t line_len;
491
+	int rc = 0;
492
+
493
+	while ( iobuf && iob_len ( iobuf ) ) {
494
+
495
+		switch ( http->rx_state ) {
496
+		case HTTP_RX_IDLE:
497
+			/* Receiving any data in this state is an error */
498
+			DBGC ( http, "HTTP %p received %zd bytes while %s\n",
499
+			       http, iob_len ( iobuf ),
500
+			       ( ( http->rx_state == HTTP_RX_IDLE ) ?
501
+				 "idle" : "dead" ) );
502
+			rc = -EPROTO;
503
+			goto done;
504
+		case HTTP_RX_DEAD:
505
+			/* Do no further processing */
506
+			goto done;
507
+		case HTTP_RX_DATA:
508
+			/* Pass received data to caller */
509
+			data_len = iob_len ( iobuf );
510
+			if ( http->chunk_remaining &&
511
+			     ( http->chunk_remaining < data_len ) ) {
512
+				data_len = http->chunk_remaining;
513
+			}
514
+			if ( http->remaining &&
515
+			     ( http->remaining < data_len ) ) {
516
+				data_len = http->remaining;
517
+			}
518
+			if ( http->rx_buffer != UNULL ) {
519
+				/* Copy to partial transfer buffer */
520
+				copy_to_user ( http->rx_buffer, http->rx_len,
521
+					       iobuf->data, data_len );
522
+				iob_pull ( iobuf, data_len );
523
+			} else if ( data_len < iob_len ( iobuf ) ) {
524
+				/* Deliver partial buffer as raw data */
525
+				rc = xfer_deliver_raw ( &http->xfer,
526
+							iobuf->data, data_len );
527
+				iob_pull ( iobuf, data_len );
528
+				if ( rc != 0 )
529
+					goto done;
530
+			} else {
531
+				/* Deliver whole I/O buffer */
532
+				if ( ( rc = xfer_deliver_iob ( &http->xfer,
533
+						 iob_disown ( iobuf ) ) ) != 0 )
534
+					goto done;
535
+			}
536
+			http->rx_len += data_len;
537
+			if ( http->chunk_remaining ) {
538
+				http->chunk_remaining -= data_len;
539
+				if ( http->chunk_remaining == 0 )
540
+					http->rx_state = HTTP_RX_CHUNK_LEN;
541
+			}
542
+			if ( http->remaining ) {
543
+				http->remaining -= data_len;
544
+				if ( ( http->remaining == 0 ) &&
545
+				     ( http->rx_state == HTTP_RX_DATA ) ) {
546
+					http_done ( http );
547
+				}
548
+			}
549
+			break;
550
+		case HTTP_RX_RESPONSE:
551
+		case HTTP_RX_HEADER:
552
+		case HTTP_RX_CHUNK_LEN:
553
+		case HTTP_RX_TRAILER:
554
+			/* In the other phases, buffer and process a
555
+			 * line at a time
556
+			 */
557
+			line_len = line_buffer ( &http->linebuf, iobuf->data,
558
+						 iob_len ( iobuf ) );
559
+			if ( line_len < 0 ) {
560
+				rc = line_len;
561
+				DBGC ( http, "HTTP %p could not buffer line: "
562
+				       "%s\n", http, strerror ( rc ) );
563
+				goto done;
564
+			}
565
+			iob_pull ( iobuf, line_len );
566
+			line = buffered_line ( &http->linebuf );
567
+			if ( line ) {
568
+				lh = &http_line_handlers[http->rx_state];
569
+				if ( ( rc = lh->rx ( http, line ) ) != 0 )
570
+					goto done;
571
+			}
572
+			break;
573
+		default:
574
+			assert ( 0 );
575
+			break;
576
+		}
577
+	}
578
+
579
+ done:
580
+	if ( rc )
581
+		http_close ( http, rc );
582
+	free_iob ( iobuf );
583
+	return rc;
584
+}
585
+
586
+/**
587
+ * Check HTTP socket flow control window
588
+ *
589
+ * @v http		HTTP request
590
+ * @ret len		Length of window
591
+ */
592
+static size_t http_socket_window ( struct http_request *http __unused ) {
593
+
594
+	/* Window is always open.  This is to prevent TCP from
595
+	 * stalling if our parent window is not currently open.
596
+	 */
597
+	return ( ~( ( size_t ) 0 ) );
598
+}
599
+
600
+/**
601
+ * HTTP process
602
+ *
603
+ * @v http		HTTP request
604
+ */
605
+static void http_step ( struct http_request *http ) {
606
+	const char *host = http->uri->host;
607
+	const char *user = http->uri->user;
608
+	const char *password =
609
+		( http->uri->password ? http->uri->password : "" );
610
+	size_t user_pw_len = ( user ? ( strlen ( user ) + 1 /* ":" */ +
611
+					strlen ( password ) ) : 0 );
612
+	size_t user_pw_base64_len = base64_encoded_len ( user_pw_len );
613
+	uint8_t user_pw[ user_pw_len + 1 /* NUL */ ];
614
+	char user_pw_base64[ user_pw_base64_len + 1 /* NUL */ ];
615
+	int rc;
616
+	int request_len = unparse_uri ( NULL, 0, http->uri,
617
+					URI_PATH_BIT | URI_QUERY_BIT );
618
+	char request[ request_len + 1 /* NUL */ ];
619
+	char range[48]; /* Enough for two 64-bit integers in decimal */
620
+	int partial;
621
+
622
+	/* Do nothing if we have already transmitted the request */
623
+	if ( ! ( http->flags & HTTP_TX_PENDING ) )
624
+		return;
625
+
626
+	/* Do nothing until socket is ready */
627
+	if ( ! xfer_window ( &http->socket ) )
628
+		return;
629
+
630
+	/* Construct path?query request */
631
+	unparse_uri ( request, sizeof ( request ), http->uri,
632
+		      URI_PATH_BIT | URI_QUERY_BIT );
633
+
634
+	/* Construct authorisation, if applicable */
635
+	if ( user ) {
636
+		/* Make "user:password" string from decoded fields */
637
+		snprintf ( ( ( char * ) user_pw ), sizeof ( user_pw ),
638
+			   "%s:%s", user, password );
639
+
640
+		/* Base64-encode the "user:password" string */
641
+		base64_encode ( user_pw, user_pw_len, user_pw_base64 );
642
+	}
643
+
644
+	/* Force a HEAD request if we have nowhere to send any received data */
645
+	if ( ( xfer_window ( &http->xfer ) == 0 ) &&
646
+	     ( http->rx_buffer == UNULL ) ) {
647
+		http->flags |= ( HTTP_HEAD_ONLY | HTTP_KEEPALIVE );
648
+	}
649
+
650
+	/* Determine type of request */
651
+	partial = ( http->partial_len != 0 );
652
+	snprintf ( range, sizeof ( range ), "%zd-%zd", http->partial_start,
653
+		   ( http->partial_start + http->partial_len - 1 ) );
654
+
655
+	/* Mark request as transmitted */
656
+	http->flags &= ~HTTP_TX_PENDING;
657
+
658
+	/* Send GET request */
659
+	if ( ( rc = xfer_printf ( &http->socket,
660
+				  "%s %s%s HTTP/1.1\r\n"
661
+				  "User-Agent: iPXE/" VERSION "\r\n"
662
+				  "Host: %s%s%s\r\n"
663
+				  "%s%s%s%s%s%s%s"
664
+				  "\r\n",
665
+				  ( ( http->flags & HTTP_HEAD_ONLY ) ?
666
+				    "HEAD" : "GET" ),
667
+				  ( http->uri->path ? "" : "/" ),
668
+				  request, host,
669
+				  ( http->uri->port ?
670
+				    ":" : "" ),
671
+				  ( http->uri->port ?
672
+				    http->uri->port : "" ),
673
+				  ( ( http->flags & HTTP_KEEPALIVE ) ?
674
+				    "Connection: Keep-Alive\r\n" : "" ),
675
+				  ( partial ? "Range: bytes=" : "" ),
676
+				  ( partial ? range : "" ),
677
+				  ( partial ? "\r\n" : "" ),
678
+				  ( user ?
679
+				    "Authorization: Basic " : "" ),
680
+				  ( user ? user_pw_base64 : "" ),
681
+				  ( user ? "\r\n" : "" ) ) ) != 0 ) {
682
+		http_close ( http, rc );
683
+	}
684
+}
685
+
686
+/**
687
+ * Check HTTP data transfer flow control window
688
+ *
689
+ * @v http		HTTP request
690
+ * @ret len		Length of window
691
+ */
692
+static size_t http_xfer_window ( struct http_request *http ) {
693
+
694
+	/* New block commands may be issued only when we are idle */
695
+	return ( ( http->rx_state == HTTP_RX_IDLE ) ? 1 : 0 );
696
+}
697
+
698
+/**
699
+ * Initiate HTTP partial read
700
+ *
701
+ * @v http		HTTP request
702
+ * @v partial		Partial transfer interface
703
+ * @v offset		Starting offset
704
+ * @v buffer		Data buffer
705
+ * @v len		Length
706
+ * @ret rc		Return status code
707
+ */
708
+static int http_partial_read ( struct http_request *http,
709
+			       struct interface *partial,
710
+			       size_t offset, userptr_t buffer, size_t len ) {
711
+
712
+	/* Sanity check */
713
+	if ( http_xfer_window ( http ) == 0 )
714
+		return -EBUSY;
715
+
716
+	/* Initialise partial transfer parameters */
717
+	http->rx_buffer = buffer;
718
+	http->partial_start = offset;
719
+	http->partial_len = len;
720
+	http->remaining = len;
721
+
722
+	/* Schedule request */
723
+	http->rx_state = HTTP_RX_RESPONSE;
724
+	http->flags = ( HTTP_TX_PENDING | HTTP_KEEPALIVE );
725
+	if ( ! len )
726
+		http->flags |= HTTP_HEAD_ONLY;
727
+	process_add ( &http->process );
728
+
729
+	/* Attach to parent interface and return */
730
+	intf_plug_plug ( &http->partial, partial );
731
+
732
+	return 0;
733
+}
734
+
735
+/**
736
+ * Issue HTTP block device read
737
+ *
738
+ * @v http		HTTP request
739
+ * @v block		Block data interface
740
+ * @v lba		Starting logical block address
741
+ * @v count		Number of blocks to transfer
742
+ * @v buffer		Data buffer
743
+ * @v len		Length of data buffer
744
+ * @ret rc		Return status code
745
+ */
746
+static int http_block_read ( struct http_request *http,
747
+			     struct interface *block,
748
+			     uint64_t lba, unsigned int count,
749
+			     userptr_t buffer, size_t len __unused ) {
750
+
751
+	return http_partial_read ( http, block, ( lba * HTTP_BLKSIZE ),
752
+				   buffer, ( count * HTTP_BLKSIZE ) );
753
+}
754
+
755
+/**
756
+ * Read HTTP block device capacity
757
+ *
758
+ * @v http		HTTP request
759
+ * @v block		Block data interface
760
+ * @ret rc		Return status code
761
+ */
762
+static int http_block_read_capacity ( struct http_request *http,
763
+				      struct interface *block ) {
764
+
765
+	return http_partial_read ( http, block, 0, 0, 0 );
766
+}
767
+
768
+/**
769
+ * Describe HTTP device in an ACPI table
770
+ *
771
+ * @v http		HTTP request
772
+ * @v acpi		ACPI table
773
+ * @v len		Length of ACPI table
774
+ * @ret rc		Return status code
775
+ */
776
+static int http_acpi_describe ( struct http_request *http,
777
+				struct acpi_description_header *acpi,
778
+				size_t len ) {
779
+
780
+	DBGC ( http, "HTTP %p cannot yet describe device in an ACPI table\n",
781
+	       http );
782
+	( void ) acpi;
783
+	( void ) len;
784
+	return 0;
785
+}
786
+
787
+/** HTTP socket interface operations */
788
+static struct interface_operation http_socket_operations[] = {
789
+	INTF_OP ( xfer_window, struct http_request *, http_socket_window ),
790
+	INTF_OP ( xfer_deliver, struct http_request *, http_socket_deliver ),
791
+	INTF_OP ( xfer_window_changed, struct http_request *, http_step ),
792
+	INTF_OP ( intf_close, struct http_request *, http_close ),
793
+};
794
+
795
+/** HTTP socket interface descriptor */
796
+static struct interface_descriptor http_socket_desc =
797
+	INTF_DESC_PASSTHRU ( struct http_request, socket,
798
+			     http_socket_operations, xfer );
799
+
800
+/** HTTP partial transfer interface operations */
801
+static struct interface_operation http_partial_operations[] = {
802
+	INTF_OP ( intf_close, struct http_request *, http_close ),
803
+};
804
+
805
+/** HTTP partial transfer interface descriptor */
806
+static struct interface_descriptor http_partial_desc =
807
+	INTF_DESC ( struct http_request, partial, http_partial_operations );
808
+
809
+/** HTTP data transfer interface operations */
810
+static struct interface_operation http_xfer_operations[] = {
811
+	INTF_OP ( xfer_window, struct http_request *, http_xfer_window ),
812
+	INTF_OP ( block_read, struct http_request *, http_block_read ),
813
+	INTF_OP ( block_read_capacity, struct http_request *,
814
+		  http_block_read_capacity ),
815
+	INTF_OP ( intf_close, struct http_request *, http_close ),
816
+	INTF_OP ( acpi_describe, struct http_request *, http_acpi_describe ),
817
+};
818
+
819
+/** HTTP data transfer interface descriptor */
820
+static struct interface_descriptor http_xfer_desc =
821
+	INTF_DESC_PASSTHRU ( struct http_request, xfer,
822
+			     http_xfer_operations, socket );
823
+
824
+/** HTTP process descriptor */
825
+static struct process_descriptor http_process_desc =
826
+	PROC_DESC_ONCE ( struct http_request, process, http_step );
827
+
828
+/**
829
+ * Initiate an HTTP connection, with optional filter
830
+ *
831
+ * @v xfer		Data transfer interface
832
+ * @v uri		Uniform Resource Identifier
833
+ * @v default_port	Default port number
834
+ * @v filter		Filter to apply to socket, or NULL
835
+ * @ret rc		Return status code
836
+ */
837
+int http_open_filter ( struct interface *xfer, struct uri *uri,
838
+		       unsigned int default_port,
839
+		       int ( * filter ) ( struct interface *xfer,
840
+					  struct interface **next ) ) {
841
+	struct http_request *http;
842
+	struct sockaddr_tcpip server;
843
+	struct interface *socket;
844
+	int rc;
845
+
846
+	/* Sanity checks */
847
+	if ( ! uri->host )
848
+		return -EINVAL;
849
+
850
+	/* Allocate and populate HTTP structure */
851
+	http = zalloc ( sizeof ( *http ) );
852
+	if ( ! http )
853
+		return -ENOMEM;
854
+	ref_init ( &http->refcnt, http_free );
855
+	intf_init ( &http->xfer, &http_xfer_desc, &http->refcnt );
856
+	intf_init ( &http->partial, &http_partial_desc, &http->refcnt );
857
+	http->uri = uri_get ( uri );
858
+	intf_init ( &http->socket, &http_socket_desc, &http->refcnt );
859
+	process_init ( &http->process, &http_process_desc, &http->refcnt );
860
+	http->flags = HTTP_TX_PENDING;
861
+
862
+	/* Open socket */
863
+	memset ( &server, 0, sizeof ( server ) );
864
+	server.st_port = htons ( uri_port ( http->uri, default_port ) );
865
+	socket = &http->socket;
866
+	if ( filter ) {
867
+		if ( ( rc = filter ( socket, &socket ) ) != 0 )
868
+			goto err;
869
+	}
870
+	if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM,
871
+					     ( struct sockaddr * ) &server,
872
+					     uri->host, NULL ) ) != 0 )
873
+		goto err;
874
+
875
+	/* Attach to parent interface, mortalise self, and return */
876
+	intf_plug_plug ( &http->xfer, xfer );
877
+	ref_put ( &http->refcnt );
878
+	return 0;
879
+
880
+ err:
881
+	DBGC ( http, "HTTP %p could not create request: %s\n",
882
+	       http, strerror ( rc ) );
883
+	http_close ( http, rc );
884
+	ref_put ( &http->refcnt );
885
+	return rc;
886
+}

Loading…
Cancel
Save