|
@@ -95,6 +95,10 @@ enum http_flags {
|
95
|
95
|
HTTP_CLIENT_KEEPALIVE = 0x0004,
|
96
|
96
|
/** Server will keep connection alive */
|
97
|
97
|
HTTP_SERVER_KEEPALIVE = 0x0008,
|
|
98
|
+ /** Discard the current request and try again */
|
|
99
|
+ HTTP_TRY_AGAIN = 0x0010,
|
|
100
|
+ /** Provide Basic authentication details */
|
|
101
|
+ HTTP_BASIC_AUTH = 0x0020,
|
98
|
102
|
};
|
99
|
103
|
|
100
|
104
|
/** HTTP receive state */
|
|
@@ -257,10 +261,11 @@ static void http_done ( struct http_request *http ) {
|
257
|
261
|
assert ( http->chunk_remaining == 0 );
|
258
|
262
|
|
259
|
263
|
/* Close partial transfer interface */
|
260
|
|
- intf_restart ( &http->partial, 0 );
|
|
264
|
+ if ( ! ( http->flags & HTTP_TRY_AGAIN ) )
|
|
265
|
+ intf_restart ( &http->partial, 0 );
|
261
|
266
|
|
262
|
267
|
/* Close everything unless we want to keep the connection alive */
|
263
|
|
- if ( ! ( http->flags & HTTP_CLIENT_KEEPALIVE ) ) {
|
|
268
|
+ if ( ! ( http->flags & ( HTTP_CLIENT_KEEPALIVE | HTTP_TRY_AGAIN ) ) ) {
|
264
|
269
|
http_close ( http, 0 );
|
265
|
270
|
return;
|
266
|
271
|
}
|
|
@@ -277,6 +282,14 @@ static void http_done ( struct http_request *http ) {
|
277
|
282
|
}
|
278
|
283
|
}
|
279
|
284
|
http->flags &= ~HTTP_SERVER_KEEPALIVE;
|
|
285
|
+
|
|
286
|
+ /* Retry the request if applicable */
|
|
287
|
+ if ( http->flags & HTTP_TRY_AGAIN ) {
|
|
288
|
+ http->flags &= ~HTTP_TRY_AGAIN;
|
|
289
|
+ http->flags |= HTTP_TX_PENDING;
|
|
290
|
+ http->rx_state = HTTP_RX_RESPONSE;
|
|
291
|
+ process_add ( &http->process );
|
|
292
|
+ }
|
280
|
293
|
}
|
281
|
294
|
|
282
|
295
|
/**
|
|
@@ -338,7 +351,7 @@ static int http_rx_response ( struct http_request *http, char *response ) {
|
338
|
351
|
* @v value HTTP header value
|
339
|
352
|
* @ret rc Return status code
|
340
|
353
|
*/
|
341
|
|
-static int http_rx_location ( struct http_request *http, const char *value ) {
|
|
354
|
+static int http_rx_location ( struct http_request *http, char *value ) {
|
342
|
355
|
int rc;
|
343
|
356
|
|
344
|
357
|
/* Redirect to new location */
|
|
@@ -360,8 +373,7 @@ static int http_rx_location ( struct http_request *http, const char *value ) {
|
360
|
373
|
* @v value HTTP header value
|
361
|
374
|
* @ret rc Return status code
|
362
|
375
|
*/
|
363
|
|
-static int http_rx_content_length ( struct http_request *http,
|
364
|
|
- const char *value ) {
|
|
376
|
+static int http_rx_content_length ( struct http_request *http, char *value ) {
|
365
|
377
|
struct block_device_capacity capacity;
|
366
|
378
|
size_t content_len;
|
367
|
379
|
char *endp;
|
|
@@ -385,6 +397,10 @@ static int http_rx_content_length ( struct http_request *http,
|
385
|
397
|
if ( ! ( http->flags & HTTP_HEAD_ONLY ) )
|
386
|
398
|
http->remaining = content_len;
|
387
|
399
|
|
|
400
|
+ /* Do nothing more if we are retrying the request */
|
|
401
|
+ if ( http->flags & HTTP_TRY_AGAIN )
|
|
402
|
+ return 0;
|
|
403
|
+
|
388
|
404
|
/* Use seek() to notify recipient of filesize */
|
389
|
405
|
xfer_seek ( &http->xfer, http->remaining );
|
390
|
406
|
xfer_seek ( &http->xfer, 0 );
|
|
@@ -406,8 +422,7 @@ static int http_rx_content_length ( struct http_request *http,
|
406
|
422
|
* @v value HTTP header value
|
407
|
423
|
* @ret rc Return status code
|
408
|
424
|
*/
|
409
|
|
-static int http_rx_transfer_encoding ( struct http_request *http,
|
410
|
|
- const char *value ) {
|
|
425
|
+static int http_rx_transfer_encoding ( struct http_request *http, char *value ){
|
411
|
426
|
|
412
|
427
|
if ( strcasecmp ( value, "chunked" ) == 0 ) {
|
413
|
428
|
/* Mark connection as using chunked transfer encoding */
|
|
@@ -424,7 +439,7 @@ static int http_rx_transfer_encoding ( struct http_request *http,
|
424
|
439
|
* @v value HTTP header value
|
425
|
440
|
* @ret rc Return status code
|
426
|
441
|
*/
|
427
|
|
-static int http_rx_connection ( struct http_request *http, const char *value ) {
|
|
442
|
+static int http_rx_connection ( struct http_request *http, char *value ) {
|
428
|
443
|
|
429
|
444
|
if ( strcasecmp ( value, "keep-alive" ) == 0 ) {
|
430
|
445
|
/* Mark connection as being kept alive by the server */
|
|
@@ -434,6 +449,88 @@ static int http_rx_connection ( struct http_request *http, const char *value ) {
|
434
|
449
|
return 0;
|
435
|
450
|
}
|
436
|
451
|
|
|
452
|
+/**
|
|
453
|
+ * Handle WWW-Authenticate Basic header
|
|
454
|
+ *
|
|
455
|
+ * @v http HTTP request
|
|
456
|
+ * @v params Parameters
|
|
457
|
+ * @ret rc Return status code
|
|
458
|
+ */
|
|
459
|
+static int http_rx_basic_auth ( struct http_request *http, char *params ) {
|
|
460
|
+
|
|
461
|
+ DBGC ( http, "HTTP %p Basic authentication required (%s)\n",
|
|
462
|
+ http, params );
|
|
463
|
+
|
|
464
|
+ /* If we received a 401 Unauthorized response, then retry
|
|
465
|
+ * using Basic authentication
|
|
466
|
+ */
|
|
467
|
+ if ( ( http->code == 401 ) &&
|
|
468
|
+ ( ! ( http->flags & HTTP_BASIC_AUTH ) ) &&
|
|
469
|
+ ( http->uri->user != NULL ) ) {
|
|
470
|
+ http->flags |= ( HTTP_TRY_AGAIN | HTTP_BASIC_AUTH );
|
|
471
|
+ }
|
|
472
|
+
|
|
473
|
+ return 0;
|
|
474
|
+}
|
|
475
|
+
|
|
476
|
+/** An HTTP WWW-Authenticate header handler */
|
|
477
|
+struct http_auth_header_handler {
|
|
478
|
+ /** Scheme (e.g. "Basic") */
|
|
479
|
+ const char *scheme;
|
|
480
|
+ /** Handle received parameters
|
|
481
|
+ *
|
|
482
|
+ * @v http HTTP request
|
|
483
|
+ * @v params Parameters
|
|
484
|
+ * @ret rc Return status code
|
|
485
|
+ */
|
|
486
|
+ int ( * rx ) ( struct http_request *http, char *params );
|
|
487
|
+};
|
|
488
|
+
|
|
489
|
+/** List of HTTP WWW-Authenticate header handlers */
|
|
490
|
+static struct http_auth_header_handler http_auth_header_handlers[] = {
|
|
491
|
+ {
|
|
492
|
+ .scheme = "Basic",
|
|
493
|
+ .rx = http_rx_basic_auth,
|
|
494
|
+ },
|
|
495
|
+ { NULL, NULL },
|
|
496
|
+};
|
|
497
|
+
|
|
498
|
+/**
|
|
499
|
+ * Handle HTTP WWW-Authenticate header
|
|
500
|
+ *
|
|
501
|
+ * @v http HTTP request
|
|
502
|
+ * @v value HTTP header value
|
|
503
|
+ * @ret rc Return status code
|
|
504
|
+ */
|
|
505
|
+static int http_rx_www_authenticate ( struct http_request *http, char *value ) {
|
|
506
|
+ struct http_auth_header_handler *handler;
|
|
507
|
+ char *separator;
|
|
508
|
+ char *scheme;
|
|
509
|
+ char *params;
|
|
510
|
+ int rc;
|
|
511
|
+
|
|
512
|
+ /* Extract scheme */
|
|
513
|
+ separator = strchr ( value, ' ' );
|
|
514
|
+ if ( ! separator ) {
|
|
515
|
+ DBGC ( http, "HTTP %p malformed WWW-Authenticate header\n",
|
|
516
|
+ http );
|
|
517
|
+ return -EINVAL_HEADER;
|
|
518
|
+ }
|
|
519
|
+ *separator = '\0';
|
|
520
|
+ scheme = value;
|
|
521
|
+ params = ( separator + 1 );
|
|
522
|
+
|
|
523
|
+ /* Hand off to header handler, if one exists */
|
|
524
|
+ for ( handler = http_auth_header_handlers; handler->scheme; handler++ ){
|
|
525
|
+ if ( strcasecmp ( scheme, handler->scheme ) == 0 ) {
|
|
526
|
+ if ( ( rc = handler->rx ( http, params ) ) != 0 )
|
|
527
|
+ return rc;
|
|
528
|
+ break;
|
|
529
|
+ }
|
|
530
|
+ }
|
|
531
|
+ return 0;
|
|
532
|
+}
|
|
533
|
+
|
437
|
534
|
/** An HTTP header handler */
|
438
|
535
|
struct http_header_handler {
|
439
|
536
|
/** Name (e.g. "Content-Length") */
|
|
@@ -446,7 +543,7 @@ struct http_header_handler {
|
446
|
543
|
*
|
447
|
544
|
* If an error is returned, the download will be aborted.
|
448
|
545
|
*/
|
449
|
|
- int ( * rx ) ( struct http_request *http, const char *value );
|
|
546
|
+ int ( * rx ) ( struct http_request *http, char *value );
|
450
|
547
|
};
|
451
|
548
|
|
452
|
549
|
/** List of HTTP header handlers */
|
|
@@ -467,6 +564,10 @@ static struct http_header_handler http_header_handlers[] = {
|
467
|
564
|
.header = "Connection",
|
468
|
565
|
.rx = http_rx_connection,
|
469
|
566
|
},
|
|
567
|
+ {
|
|
568
|
+ .header = "WWW-Authenticate",
|
|
569
|
+ .rx = http_rx_www_authenticate,
|
|
570
|
+ },
|
470
|
571
|
{ NULL, NULL }
|
471
|
572
|
};
|
472
|
573
|
|
|
@@ -488,8 +589,10 @@ static int http_rx_header ( struct http_request *http, char *header ) {
|
488
|
589
|
empty_line_buffer ( &http->linebuf );
|
489
|
590
|
|
490
|
591
|
/* Handle response code */
|
491
|
|
- if ( ( rc = http_response_to_rc ( http->code ) ) != 0 )
|
492
|
|
- return rc;
|
|
592
|
+ if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) {
|
|
593
|
+ if ( ( rc = http_response_to_rc ( http->code ) ) != 0 )
|
|
594
|
+ return rc;
|
|
595
|
+ }
|
493
|
596
|
|
494
|
597
|
/* Move to next state */
|
495
|
598
|
if ( ( http->rx_state == HTTP_RX_HEADER ) &&
|
|
@@ -497,6 +600,10 @@ static int http_rx_header ( struct http_request *http, char *header ) {
|
497
|
600
|
DBGC ( http, "HTTP %p start of data\n", http );
|
498
|
601
|
http->rx_state = ( http->chunked ?
|
499
|
602
|
HTTP_RX_CHUNK_LEN : HTTP_RX_DATA );
|
|
603
|
+ if ( ( http->partial_len != 0 ) &&
|
|
604
|
+ ( ! ( http->flags & HTTP_TRY_AGAIN ) ) ) {
|
|
605
|
+ http->remaining = http->partial_len;
|
|
606
|
+ }
|
500
|
607
|
return 0;
|
501
|
608
|
} else {
|
502
|
609
|
DBGC ( http, "HTTP %p end of trailer\n", http );
|
|
@@ -560,8 +667,11 @@ static int http_rx_chunk_len ( struct http_request *http, char *length ) {
|
560
|
667
|
/* Use seek() to notify recipient of new filesize */
|
561
|
668
|
DBGC ( http, "HTTP %p start of chunk of length %zd\n",
|
562
|
669
|
http, http->chunk_remaining );
|
563
|
|
- xfer_seek ( &http->xfer, ( http->rx_len + http->chunk_remaining ) );
|
564
|
|
- xfer_seek ( &http->xfer, http->rx_len );
|
|
670
|
+ if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) {
|
|
671
|
+ xfer_seek ( &http->xfer,
|
|
672
|
+ ( http->rx_len + http->chunk_remaining ) );
|
|
673
|
+ xfer_seek ( &http->xfer, http->rx_len );
|
|
674
|
+ }
|
565
|
675
|
|
566
|
676
|
/* Start receiving data */
|
567
|
677
|
http->rx_state = HTTP_RX_DATA;
|
|
@@ -630,7 +740,10 @@ static int http_socket_deliver ( struct http_request *http,
|
630
|
740
|
( http->remaining < data_len ) ) {
|
631
|
741
|
data_len = http->remaining;
|
632
|
742
|
}
|
633
|
|
- if ( http->rx_buffer != UNULL ) {
|
|
743
|
+ if ( http->flags & HTTP_TRY_AGAIN ) {
|
|
744
|
+ /* Discard all received data */
|
|
745
|
+ iob_pull ( iobuf, data_len );
|
|
746
|
+ } else if ( http->rx_buffer != UNULL ) {
|
634
|
747
|
/* Copy to partial transfer buffer */
|
635
|
748
|
copy_to_user ( http->rx_buffer, http->rx_len,
|
636
|
749
|
iobuf->data, data_len );
|
|
@@ -830,7 +943,7 @@ static void http_step ( struct http_request *http ) {
|
830
|
943
|
}
|
831
|
944
|
|
832
|
945
|
/* Construct authorisation, if applicable */
|
833
|
|
- if ( http->uri->user ) {
|
|
946
|
+ if ( http->flags & HTTP_BASIC_AUTH ) {
|
834
|
947
|
auth = http_basic_auth ( http );
|
835
|
948
|
if ( ! auth ) {
|
836
|
949
|
rc = -ENOMEM;
|
|
@@ -907,7 +1020,6 @@ static int http_partial_read ( struct http_request *http,
|
907
|
1020
|
http->rx_buffer = buffer;
|
908
|
1021
|
http->partial_start = offset;
|
909
|
1022
|
http->partial_len = len;
|
910
|
|
- http->remaining = len;
|
911
|
1023
|
|
912
|
1024
|
/* Schedule request */
|
913
|
1025
|
http->rx_state = HTTP_RX_RESPONSE;
|