4 #ifdef DOWNLOAD_PROTO_HTTP
6 /* The block size is currently chosen to be 512 bytes. This means, we can
7 allocate the receive buffer on the stack, but it results in a noticeable
9 This is what needs to be done in order to increase the block size:
10 - size negotiation needs to be implemented in TCP
11 - the buffer needs to be allocated on the heap
12 - path MTU discovery needs to be implemented
14 #define BLOCKSIZE TFTP_DEFAULTSIZE_PACKET
16 /**************************************************************************
17 SEND_TCP_CALLBACK - Send data using TCP
18 **************************************************************************/
19 struct send_recv_state {
20 int (*fnc)(unsigned char *data, int block, int len, int eof);
28 enum { RESULT_CODE, HEADER, DATA, ERROR, MOVED } recv_state;
30 char location[MAX_URL+1];
33 static int send_tcp_request(int length, void *buffer, void *ptr) {
34 struct send_recv_state *state = (struct send_recv_state *)ptr;
36 if (length > state->send_length - state->bytes_sent)
37 length = state->send_length - state->bytes_sent;
38 memcpy(buffer, state->send_buffer + state->bytes_sent, length);
39 state->bytes_sent += length;
43 /**************************************************************************
44 RECV_TCP_CALLBACK - Receive data using TCP
45 **************************************************************************/
46 static int recv_tcp_request(int length, const void *buffer, void *ptr) {
47 struct send_recv_state *state = (struct send_recv_state *)ptr;
49 /* Assume that the lines in an HTTP header do not straddle a packet */
50 /* boundary. This is probably a reasonable assumption */
51 if (state->recv_state == RESULT_CODE) {
53 /* Find HTTP result code */
54 if (*(const char *)buffer == ' ') {
55 const char *ptr = ((const char *)buffer) + 1;
56 int rc = strtoul(ptr, &ptr, 10);
57 if (ptr >= (const char *)buffer + length) {
58 state->recv_state = ERROR;
62 state->recv_state = HEADER;
65 ++(const char *)buffer;
68 state->recv_state = ERROR;
71 if (state->recv_state == HEADER) {
72 header: while (length > 0) {
73 /* Check for HTTP redirect */
74 if (state->rc >= 300 && state->rc < 400 &&
75 !memcmp(buffer, "Location: ", 10)) {
76 char *ptr = state->location;
78 memcpy(ptr, buffer + 10, MAX_URL);
79 for (i = 0; i < MAX_URL && *ptr > ' ';
82 state->recv_state = MOVED;
85 /* Find beginning of line */
88 if (*((const char *)buffer)++ == '\n')
91 /* Check for end of header */
92 if (length >= 2 && !memcmp(buffer, "\r\n", 2)) {
93 state->recv_state = DATA;
100 if (state->recv_state == DATA) {
101 state->bytes_received += length;
103 int copy_length = BLOCKSIZE - state->recv_length;
104 if (copy_length > length)
105 copy_length = length;
106 memcpy(state->recv_buffer + state->recv_length,
107 buffer, copy_length);
108 if ((state->recv_length += copy_length) == BLOCKSIZE) {
109 if (!state->fnc(state->recv_buffer,
110 ++state->block, BLOCKSIZE, 0))
112 state->recv_length = 0;
114 length -= copy_length;
115 buffer += copy_length;
121 /**************************************************************************
122 HTTP_GET - Get data using HTTP
123 **************************************************************************/
124 int http(const char *url,
125 int (*fnc)(unsigned char *, unsigned int, unsigned int, int)) {
126 static const char GET[] = "GET /%s HTTP/1.0\r\n\r\n";
127 static char recv_buffer[BLOCKSIZE];
131 struct send_recv_state state;
136 state.recv_buffer = recv_buffer;
137 length = strlen(url);
138 if (length <= MAX_URL) {
139 memcpy(state.location, url, length+1);
140 destip = arptable[ARP_SERVER].ipaddr;
149 url = state.location;
150 if (memcmp("http://", url, 7))
153 length = inet_aton(url, &destip);
155 /* As we do not have support for DNS, assume*/
156 /* that HTTP redirects always point to the */
158 if (state.recv_state == MOVED) {
160 *url != ':' && *url != '/') url++;
165 if (*(url += length) == ':') {
166 port = strtoul(url, &url, 10);
177 length = strlen(url);
178 state.send_length = sizeof(GET) - 3 + length;
180 { char buf[state.send_length + 1];
181 sprintf(state.send_buffer = buf, GET, url);
182 state.bytes_sent = 0;
184 state.bytes_received = 0;
185 state.recv_state = RESULT_CODE;
187 state.recv_length = 0;
188 tcp_transaction(destip.s_addr, 80, &state,
189 send_tcp_request, recv_tcp_request);
191 } while (state.recv_state == MOVED);
193 memcpy(state.location, url, MAX_URL);
194 state.location[MAX_URL] = '\000';
197 if (state.rc == 200) {
198 return fnc(recv_buffer, ++state.block, state.recv_length, 1);
200 printf("Failed to download %s (rc = %d)\n",
201 state.location, state.rc);
206 #endif /* DOWNLOAD_PROTO_HTTP */