diff --git a/ChangeLog b/ChangeLog index 846b69d5f..c12da1a0c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2012-06-22 Vladimir Serbinenko + + Implement flow control for http. + + * grub-core/net/http.c (parse_line): Handle response 206. + (http_receive): Stall if too many packets are in the queue. + (http_establish): Fix range header. + (http_seek): Fix double free. + (http_close): Likewise. + (http_packets_pulled): New function. + (grub_http_protocol): Set http_seek + * grub-core/net/tcp.c (grub_net_tcp_socket): New field `i_stall'. + (ack_real): Set window depending on i_stall. + (grub_net_send_tcp_packet): Likewise. + (grub_net_tcp_stall): New function. + (grub_net_tcp_unstall): Likewise. + * include/grub/net/tcp.h (grub_net_tcp_stall): New proto. + (grub_net_tcp_unstall): Likewise. + 2012-06-22 Vladimir Serbinenko * grub-core/net/tftp.c: Decrease stall to 50 packets. diff --git a/grub-core/net/http.c b/grub-core/net/http.c index fc8f3babd..a7542d1ae 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -101,8 +101,11 @@ parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len) { int code; if (grub_memcmp (ptr, "HTTP/1.1 ", sizeof ("HTTP/1.1 ") - 1) != 0) - return grub_error (GRUB_ERR_NET_INVALID_RESPONSE, - N_("unsupported HTTP response")); + { + data->errmsg = grub_strdup (_("unsupported HTTP response")); + data->first_line_recv = 1; + return GRUB_ERR_NONE; + } ptr += sizeof ("HTTP/1.1 ") - 1; code = grub_strtoul (ptr, &ptr, 10); if (grub_errno) @@ -110,6 +113,7 @@ parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len) switch (code) { case 200: + case 206: break; case 404: data->err = GRUB_ERR_FILE_NOT_FOUND; @@ -262,6 +266,12 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), < nb->tail - nb->data)) { grub_net_put_packet (&file->device->net->packs, nb); + if (file->device->net->packs.count >= 20) + file->device->net->stall = 1; + + if (file->device->net->packs.count >= 100) + grub_net_tcp_stall (data->sock); + if (data->chunked) data->chunk_rem -= nb->tail - nb->data; return GRUB_ERR_NONE; @@ -274,6 +284,12 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), return grub_errno; grub_netbuff_put (nb2, data->chunk_rem); grub_memcpy (nb2->data, nb->data, data->chunk_rem); + if (file->device->net->packs.count >= 20) + { + file->device->net->stall = 1; + grub_net_tcp_stall (data->sock); + } + grub_net_put_packet (&file->device->net->packs, nb2); grub_netbuff_pull (nb, data->chunk_rem); } @@ -297,9 +313,8 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) + grub_strlen (file->device->net->server) + sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n") - 1 - + sizeof ("Content-Range: bytes XXXXXXXXXXXXXXXXXXXX" - "-XXXXXXXXXXXXXXXXXXXX/" - "XXXXXXXXXXXXXXXXXXXX\r\n\r\n")); + + sizeof ("Range: bytes=XXXXXXXXXXXXXXXXXXXX" + "-\r\n\r\n")); if (!nb) return grub_errno; @@ -358,12 +373,11 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) { ptr = nb->tail; grub_snprintf ((char *) ptr, - sizeof ("Content-Range: bytes XXXXXXXXXXXXXXXXXXXX-" - "XXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXX\r\n" + sizeof ("Range: bytes=XXXXXXXXXXXXXXXXXXXX-" + "\r\n" "\r\n"), - "Content-Range: bytes %" PRIuGRUB_UINT64_T "-%" - PRIuGRUB_UINT64_T "/%" PRIuGRUB_UINT64_T "\r\n\r\n", - offset, file->size - 1, file->size); + "Range: bytes=%" PRIuGRUB_UINT64_T "-\r\n\r\n", + offset); grub_netbuff_put (nb, grub_strlen ((char *) ptr)); } ptr = nb->tail; @@ -403,6 +417,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) char *str = data->errmsg; err = grub_error (data->err, "%s", str); grub_free (str); + data->errmsg = 0; return data->err; } return grub_error (GRUB_ERR_TIMEOUT, N_("time out opening `%s'"), data->filename); @@ -418,6 +433,7 @@ http_seek (struct grub_file *file, grub_off_t off) old_data = file->data; /* FIXME: Reuse socket? */ grub_net_tcp_close (old_data->sock, GRUB_NET_TCP_ABORT); + old_data->sock = 0; while (file->device->net->packs.first) { @@ -425,6 +441,7 @@ http_seek (struct grub_file *file, grub_off_t off) grub_net_remove_packet (file->device->net->packs.first); } + file->device->net->stall = 0; file->device->net->offset = off; data = grub_zalloc (sizeof (*data)); @@ -436,6 +453,7 @@ http_seek (struct grub_file *file, grub_off_t off) if (!data->filename) { grub_free (data); + file->data = 0; return grub_errno; } grub_free (old_data); @@ -446,6 +464,7 @@ http_seek (struct grub_file *file, grub_off_t off) { grub_free (data->filename); grub_free (data); + file->data = 0; return err; } return GRUB_ERR_NONE; @@ -488,20 +507,39 @@ http_close (struct grub_file *file) { http_data_t data = file->data; + if (!data) + return GRUB_ERR_NONE; + if (data->sock) grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); if (data->current_line) grub_free (data->current_line); + grub_free (data->filename); grub_free (data); return GRUB_ERR_NONE; } +static grub_err_t +http_packets_pulled (struct grub_file *file) +{ + http_data_t data = file->data; + + if (file->device->net->packs.count >= 20) + return 0; + + if (!file->device->net->eof) + file->device->net->stall = 0; + grub_net_tcp_unstall (data->sock); + return 0; +} + static struct grub_net_app_protocol grub_http_protocol = { .name = "http", .open = http_open, .close = http_close, - .seek = http_seek + .seek = http_seek, + .packets_pulled = http_packets_pulled }; GRUB_MOD_INIT (http) diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 57fdba979..e7112a7b7 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -60,6 +60,7 @@ struct grub_net_tcp_socket int errors; int they_reseted; int i_reseted; + int i_stall; grub_uint32_t my_start_seq; grub_uint32_t my_cur_seq; grub_uint32_t their_start_seq; @@ -331,7 +332,8 @@ ack_real (grub_net_tcp_socket_t sock, int res) { tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq); tcph_ack->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK); - tcph_ack->window = grub_cpu_to_be16 (sock->my_window); + tcph_ack->window = !sock->i_stall ? grub_cpu_to_be16 (sock->my_window) + : 0; } tcph_ack->urgent = 0; tcph_ack->src = grub_cpu_to_be16 (sock->in_port); @@ -707,7 +709,8 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, tcph = (struct tcphdr *) nb2->data; tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq); tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK); - tcph->window = grub_cpu_to_be16 (socket->my_window); + tcph->window = !socket->i_stall ? grub_cpu_to_be16 (socket->my_window) + : 0; tcph->urgent = 0; err = grub_netbuff_put (nb2, fraglen); if (err) @@ -730,7 +733,7 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq); tcph->flags = (grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK) | (push ? grub_cpu_to_be16_compile_time (TCP_PUSH) : 0)); - tcph->window = grub_cpu_to_be16 (socket->my_window); + tcph->window = !socket->i_stall ? grub_cpu_to_be16 (socket->my_window) : 0; tcph->urgent = 0; return tcp_send (nb, socket); } @@ -975,3 +978,21 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, grub_netbuff_free (nb); return GRUB_ERR_NONE; } + +void +grub_net_tcp_stall (grub_net_tcp_socket_t sock) +{ + if (sock->i_stall) + return; + sock->i_stall = 1; + ack (sock); +} + +void +grub_net_tcp_unstall (grub_net_tcp_socket_t sock) +{ + if (!sock->i_stall) + return; + sock->i_stall = 0; + ack (sock); +} diff --git a/include/grub/net/tcp.h b/include/grub/net/tcp.h index 62bfd2eba..5a9305ab8 100644 --- a/include/grub/net/tcp.h +++ b/include/grub/net/tcp.h @@ -76,4 +76,10 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, void *data), void *hook_data); +void +grub_net_tcp_stall (grub_net_tcp_socket_t sock); + +void +grub_net_tcp_unstall (grub_net_tcp_socket_t sock); + #endif