Skip to content
Snippets Groups Projects
Commit 4f71e077 authored by Vladimir 'phcoder' Serbinenko's avatar Vladimir 'phcoder' Serbinenko
Browse files

HTTP seek support. Various bugfixes.

parent aa6b91af
No related branches found
No related tags found
No related merge requests found
......@@ -42,7 +42,11 @@ typedef struct http_data
grub_size_t current_line_len;
int headers_recv;
int first_line_recv;
int size_recv;
grub_net_tcp_socket_t sock;
char *filename;
grub_err_t err;
char *errmsg;
} *http_data_t;
static grub_err_t
......@@ -72,19 +76,25 @@ parse_line (http_data_t data, char *ptr, grub_size_t len)
{
case 200:
break;
case 404:
data->err = GRUB_ERR_FILE_NOT_FOUND;
data->errmsg = grub_xasprintf ("file `%s' not found", data->filename);
return GRUB_ERR_NONE;
default:
return grub_error (GRUB_ERR_NET_UNKNOWN_ERROR,
"unsupported HTTP error %d: %s",
code, ptr);
data->err = GRUB_ERR_NET_UNKNOWN_ERROR;
data->errmsg = grub_xasprintf ("unsupported HTTP error %d: %s",
code, ptr);
return GRUB_ERR_NONE;
}
data->first_line_recv = 1;
return GRUB_ERR_NONE;
}
if (grub_memcmp (ptr, "Content-Length: ", sizeof ("Content-Length: ") - 1)
== 0)
== 0 && !data->size_recv)
{
ptr += sizeof ("Content-Length: ") - 1;
data->file_size = grub_strtoull (ptr, &ptr, 10);
data->size_recv = 1;
return GRUB_ERR_NONE;
}
return GRUB_ERR_NONE;
......@@ -98,10 +108,10 @@ http_err (grub_net_tcp_socket_t sock __attribute__ ((unused)),
http_data_t data = file->data;
if (data->sock)
grub_net_tcp_close (data->sock);
grub_free (data);
grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
if (data->current_line)
grub_free (data->current_line);
grub_free (data);
file->device->net->eof = 1;
}
......@@ -132,7 +142,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)),
if (!t)
{
grub_netbuff_free (nb);
grub_net_tcp_close (data->sock);
grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
return grub_errno;
}
......@@ -151,7 +161,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)),
data->current_line_len = 0;
if (err)
{
grub_net_tcp_close (data->sock);
grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
grub_netbuff_free (nb);
return err;
}
......@@ -167,7 +177,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)),
if (!data->current_line)
{
grub_netbuff_free (nb);
grub_net_tcp_close (data->sock);
grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
return grub_errno;
}
data->current_line_len = (char *) nb->tail - ptr;
......@@ -178,7 +188,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)),
err = parse_line (data, ptr, ptr2 - ptr);
if (err)
{
grub_net_tcp_close (data->sock);
grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
grub_netbuff_free (nb);
return err;
}
......@@ -191,7 +201,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)),
err = grub_netbuff_pull (nb, ptr - (char *) nb->data);
if (err)
{
grub_net_tcp_close (data->sock);
grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
grub_netbuff_free (nb);
return err;
}
......@@ -203,57 +213,51 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)),
}
static grub_err_t
http_open (struct grub_file *file, const char *filename)
http_establish (struct grub_file *file, grub_off_t offset, int initial)
{
struct grub_net_buff *nb;
http_data_t data;
grub_err_t err;
http_data_t data = file->data;
grub_uint8_t *ptr;
int i;
data = grub_zalloc (sizeof (*data));
if (!data)
return grub_errno;
struct grub_net_buff *nb;
grub_err_t err;
nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE
+ sizeof ("GET ") - 1
+ grub_strlen (filename)
+ grub_strlen (data->filename)
+ sizeof (" HTTP/1.1\r\nHost: ") - 1
+ grub_strlen (file->device->net->server)
+ sizeof ("\r\nUser-Agent: " PACKAGE_STRING
"\r\n\r\n") - 1);
"\r\n") - 1
+ sizeof ("Content-Range: bytes XXXXXXXXXXXXXXXXXXXX"
"-XXXXXXXXXXXXXXXXXXXX/"
"XXXXXXXXXXXXXXXXXXXX\r\n\r\n"));
if (!nb)
{
grub_free (data);
return grub_errno;
}
return grub_errno;
grub_netbuff_reserve (nb, GRUB_NET_TCP_RESERVE_SIZE);
ptr = nb->tail;
err = grub_netbuff_put (nb, sizeof ("GET ") - 1);
if (err)
{
grub_free (data);
grub_netbuff_free (nb);
return err;
}
grub_memcpy (ptr, "GET ", sizeof ("GET ") - 1);
ptr = nb->tail;
err = grub_netbuff_put (nb, grub_strlen (filename));
err = grub_netbuff_put (nb, grub_strlen (data->filename));
if (err)
{
grub_free (data);
grub_netbuff_free (nb);
return err;
}
grub_memcpy (ptr, filename, grub_strlen (filename));
grub_memcpy (ptr, data->filename, grub_strlen (data->filename));
ptr = nb->tail;
err = grub_netbuff_put (nb, sizeof (" HTTP/1.1\r\nHost: ") - 1);
if (err)
{
grub_free (data);
grub_netbuff_free (nb);
return err;
}
......@@ -264,7 +268,6 @@ http_open (struct grub_file *file, const char *filename)
err = grub_netbuff_put (nb, grub_strlen (file->device->net->server));
if (err)
{
grub_free (data);
grub_netbuff_free (nb);
return err;
}
......@@ -273,19 +276,30 @@ http_open (struct grub_file *file, const char *filename)
ptr = nb->tail;
err = grub_netbuff_put (nb,
sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n")
sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n")
- 1);
if (err)
{
grub_free (data);
grub_netbuff_free (nb);
return err;
}
grub_memcpy (ptr, "\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n",
sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n") - 1);
file->not_easily_seekable = 1;
file->data = data;
grub_memcpy (ptr, "\r\nUser-Agent: " PACKAGE_STRING "\r\n",
sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n") - 1);
if (!initial)
{
ptr = nb->tail;
grub_snprintf ((char *) ptr,
sizeof ("Content-Range: bytes XXXXXXXXXXXXXXXXXXXX-"
"XXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXX\r\n"
"\r\n"),
"Content-Range: bytes %" PRIuGRUB_UINT64_T "-%"
PRIuGRUB_UINT64_T "/%" PRIuGRUB_UINT64_T "\r\n\r\n",
offset, data->file_size - 1, data->file_size);
grub_netbuff_put (nb, grub_strlen ((char *) ptr));
}
ptr = nb->tail;
grub_netbuff_put (nb, 2);
grub_memcpy (ptr, "\r\n", 2);
data->sock = grub_net_tcp_open (file->device->net->server,
HTTP_PORT, http_receive,
......@@ -293,7 +307,6 @@ http_open (struct grub_file *file, const char *filename)
file);
if (!data->sock)
{
grub_free (data);
grub_netbuff_free (nb);
return grub_errno;
}
......@@ -303,8 +316,7 @@ http_open (struct grub_file *file, const char *filename)
err = grub_net_send_tcp_packet (data->sock, nb, 1);
if (err)
{
grub_free (data);
grub_net_tcp_close (data->sock);
grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
return err;
}
......@@ -316,9 +328,81 @@ http_open (struct grub_file *file, const char *filename)
if (!data->headers_recv)
{
grub_net_tcp_close (data->sock);
grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
if (data->err)
{
char *str = data->errmsg;
err = grub_error (data->err, "%s", str);
grub_free (str);
return data->err;
}
return grub_error (GRUB_ERR_TIMEOUT, "timeout opening http");
}
return GRUB_ERR_NONE;
}
static grub_err_t
http_seek (struct grub_file *file, grub_off_t off)
{
struct http_data *old_data, *data;
grub_err_t err;
old_data = file->data;
/* FIXME: Reuse socket? */
grub_net_tcp_close (old_data->sock, GRUB_NET_TCP_ABORT);
while (file->device->net->packs.first)
grub_net_remove_packet (file->device->net->packs.first);
data = grub_zalloc (sizeof (*data));
if (!data)
return grub_errno;
data->file_size = old_data->file_size;
data->size_recv = 1;
data->filename = old_data->filename;
if (!data->filename)
{
grub_free (data);
return grub_error (GRUB_ERR_TIMEOUT, "Time out opening http.");
return grub_errno;
}
grub_free (old_data);
err = http_establish (file, off, 0);
if (err)
{
grub_free (data->filename);
grub_free (data);
return err;
}
return GRUB_ERR_NONE;
}
static grub_err_t
http_open (struct grub_file *file, const char *filename)
{
grub_err_t err;
struct http_data *data;
data = grub_zalloc (sizeof (*data));
if (!data)
return grub_errno;
data->filename = grub_strdup (filename);
if (!data->filename)
{
grub_free (data);
return grub_errno;
}
file->not_easily_seekable = 0;
file->data = data;
err = http_establish (file, 0, 1);
if (err)
{
grub_free (data->filename);
grub_free (data);
return err;
}
file->size = data->file_size;
......@@ -331,10 +415,10 @@ http_close (struct grub_file *file)
http_data_t data = file->data;
if (data->sock)
grub_net_tcp_close (data->sock);
grub_free (data);
grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
if (data->current_line)
grub_free (data->current_line);
grub_free (data);
return GRUB_ERR_NONE;
}
......@@ -342,7 +426,8 @@ static struct grub_net_app_protocol grub_http_protocol =
{
.name = "http",
.open = http_open,
.close = http_close
.close = http_close,
.seek = http_seek
};
GRUB_MOD_INIT (http)
......
......@@ -815,17 +815,17 @@ grub_net_poll_cards_idle_real (void)
static grub_ssize_t
grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len)
{
grub_net_t sock = file->device->net;
grub_net_t net = file->device->net;
struct grub_net_buff *nb;
char *ptr = buf;
grub_size_t amount, total = 0;
int try = 0;
while (try <= 3)
{
while (sock->packs.first)
while (net->packs.first)
{
try = 0;
nb = sock->packs.first->nb;
nb = net->packs.first->nb;
amount = nb->tail - nb->data;
if (amount > len)
amount = len;
......@@ -840,7 +840,7 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len)
if (amount == (grub_size_t) (nb->tail - nb->data))
{
grub_netbuff_free (nb);
grub_net_remove_packet (sock->packs.first);
grub_net_remove_packet (net->packs.first);
}
else
nb->data += amount;
......@@ -848,7 +848,7 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len)
if (!len)
return total;
}
if (!sock->eof)
if (!net->eof)
{
try++;
grub_net_poll_cards (200);
......@@ -856,39 +856,59 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len)
else
return total;
}
return total;
grub_error (GRUB_ERR_TIMEOUT, "timeout reading '%s'", net->name);
return -1;
}
static grub_off_t
have_ahead (struct grub_file *file)
{
grub_net_t net = file->device->net;
grub_off_t ret = net->offset;
struct grub_net_packet *pack;
for (pack = net->packs.first; pack; pack = pack->next)
ret += pack->nb->tail - pack->nb->data;
return ret;
}
static grub_err_t
grub_net_seek_real (struct grub_file *file, grub_off_t offset)
{
grub_size_t len = offset - file->device->net->offset;
if (!len)
if (offset == file->device->net->offset)
return GRUB_ERR_NONE;
if (file->device->net->offset > offset)
if (offset > file->device->net->offset)
{
grub_err_t err;
while (file->device->net->packs.first)
if (!file->device->net->protocol->seek || have_ahead (file) >= offset)
{
grub_netbuff_free (file->device->net->packs.first->nb);
grub_net_remove_packet (file->device->net->packs.first);
grub_net_fs_read_real (file, NULL,
offset - file->device->net->offset);
return grub_errno;
}
file->device->net->protocol->close (file);
file->device->net->packs.first = NULL;
file->device->net->packs.last = NULL;
file->device->net->offset = 0;
file->device->net->eof = 0;
err = file->device->net->protocol->open (file, file->device->net->name);
if (err)
return err;
len = offset;
return file->device->net->protocol->seek (file, offset);
}
grub_net_fs_read_real (file, NULL, len);
return GRUB_ERR_NONE;
{
grub_err_t err;
if (file->device->net->protocol->seek)
return file->device->net->protocol->seek (file, offset);
while (file->device->net->packs.first)
{
grub_netbuff_free (file->device->net->packs.first->nb);
grub_net_remove_packet (file->device->net->packs.first);
}
file->device->net->protocol->close (file);
file->device->net->packs.first = NULL;
file->device->net->packs.last = NULL;
file->device->net->offset = 0;
file->device->net->eof = 0;
err = file->device->net->protocol->open (file, file->device->net->name);
if (err)
return err;
grub_net_fs_read_real (file, NULL, offset);
return grub_errno;
}
}
static grub_ssize_t
......
......@@ -56,7 +56,8 @@ struct grub_net_tcp_socket
int in_port;
int out_port;
int errors;
int reseted;
int they_reseted;
int i_reseted;
grub_uint32_t my_start_seq;
grub_uint32_t my_cur_seq;
grub_uint32_t their_start_seq;
......@@ -153,7 +154,7 @@ error (grub_net_tcp_socket_t sock)
{
struct unacked *unack, *next;
if (sock->established && sock->error_hook)
if (sock->error_hook)
sock->error_hook (sock, sock->hook_data);
for (unack = sock->unack_first; unack; unack = next)
......@@ -217,7 +218,8 @@ tcp_send (struct grub_net_buff *nb, grub_net_tcp_socket_t socket)
}
void
grub_net_tcp_close (grub_net_tcp_socket_t sock)
grub_net_tcp_close (grub_net_tcp_socket_t sock,
int discard_received)
{
struct grub_net_buff *nb_fin;
struct tcphdr *tcph_fin;
......@@ -225,6 +227,9 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock)
sock->i_closed = 1;
if (discard_received != GRUB_NET_TCP_CONTINUE_RECEIVING)
sock->recv_hook = NULL;
nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin)
+ GRUB_NET_OUR_IPV4_HEADER_SIZE
+ GRUB_NET_MAX_LINK_HEADER_SIZE);
......@@ -254,6 +259,8 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock)
tcph_fin->window = grub_cpu_to_be16 (0);
tcph_fin->urgent = 0;
err = tcp_send (nb_fin, sock);
if (discard_received == GRUB_NET_TCP_ABORT)
sock->i_reseted = 1;
if (err)
{
grub_netbuff_free (nb_fin);
......@@ -264,7 +271,7 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock)
}
static void
ack (grub_net_tcp_socket_t sock)
ack_real (grub_net_tcp_socket_t sock, int res)
{
struct grub_net_buff *nb_ack;
struct tcphdr *tcph_ack;
......@@ -291,9 +298,18 @@ ack (grub_net_tcp_socket_t sock)
return;
}
tcph_ack = (void *) nb_ack->data;
tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq);
tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_ACK);
tcph_ack->window = grub_cpu_to_be16 (sock->my_window);
if (res)
{
tcph_ack->ack = grub_cpu_to_be32 (0);
tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_RST);
tcph_ack->window = grub_cpu_to_be16 (0);
}
else
{
tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq);
tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_ACK);
tcph_ack->window = grub_cpu_to_be16 (sock->my_window);
}
tcph_ack->urgent = 0;
tcph_ack->src = grub_cpu_to_be16 (sock->in_port);
tcph_ack->dst = grub_cpu_to_be16 (sock->out_port);
......@@ -305,6 +321,18 @@ ack (grub_net_tcp_socket_t sock)
}
}
static void
ack (grub_net_tcp_socket_t sock)
{
ack_real (sock, 0);
}
static void
reset (grub_net_tcp_socket_t sock)
{
ack_real (sock, 1);
}
void
grub_net_tcp_retransmit (void)
{
......@@ -434,6 +462,7 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock,
tcph->flags = grub_cpu_to_be16 ((5 << 12) | TCP_SYN | TCP_ACK);
tcph->window = grub_cpu_to_be16 (sock->my_window);
tcph->urgent = 0;
sock->established = 1;
tcp_socket_register (sock);
err = tcp_send (nb_ack, sock);
if (err)
......@@ -555,7 +584,7 @@ grub_net_tcp_open (char *server,
{
grub_list_remove (GRUB_AS_LIST_P (&tcp_sockets),
GRUB_AS_LIST (socket));
if (socket->reseted)
if (socket->they_reseted)
grub_error (GRUB_ERR_NET_PORT_CLOSED, "port closed");
else
grub_error (GRUB_ERR_NET_NO_ANSWER, "no answer");
......@@ -693,7 +722,7 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb,
if (grub_be_to_cpu16 (tcph->flags) & TCP_RST)
{
sock->reseted = 1;
sock->they_reseted = 1;
error (sock);
......@@ -725,7 +754,7 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb,
grub_free (unack);
}
sock->unack_first = unack;
if (!sock->unack_last)
if (!sock->unack_first)
sock->unack_last = NULL;
}
......@@ -735,6 +764,12 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb,
grub_netbuff_free (nb);
return GRUB_ERR_NONE;
}
if (sock->i_reseted)
{
reset (sock);
grub_netbuff_free (nb);
return GRUB_ERR_NONE;
}
err = grub_priority_queue_push (sock->pq, &nb);
if (err)
......
......@@ -204,6 +204,7 @@ struct grub_net_app_protocol
int (*hook) (const char *filename,
const struct grub_dirhook_info *info));
grub_err_t (*open) (struct grub_file *file, const char *filename);
grub_err_t (*seek) (struct grub_file *file, grub_off_t off);
grub_err_t (*close) (struct grub_file *file);
};
......
......@@ -53,8 +53,15 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket,
struct grub_net_buff *nb,
int push);
enum
{
GRUB_NET_TCP_CONTINUE_RECEIVING,
GRUB_NET_TCP_DISCARD,
GRUB_NET_TCP_ABORT
};
void
grub_net_tcp_close (grub_net_tcp_socket_t sock);
grub_net_tcp_close (grub_net_tcp_socket_t sock, int discard_received);
grub_err_t
grub_net_tcp_accept (grub_net_tcp_socket_t sock,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment