gcc is rather over-aggressive about optimising out static data structures
[people/asdlkf/gpxe.git] / src / proto / http.c
1 #include "proto.h"
2 #include "old_tcp.h"
3 #include "url.h"
4 #include "etherboot.h"
5
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
8    performance penalty.
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
13 */ /***/ /* FIXME */
14 #define BLOCKSIZE TFTP_DEFAULTSIZE_PACKET
15
16 /**************************************************************************
17 SEND_TCP_CALLBACK - Send data using TCP
18 **************************************************************************/
19 struct send_recv_state {
20         struct buffer *recv_buffer;
21         char *send_buffer;
22         int send_length;
23         int bytes_sent;
24         int bytes_received;
25         enum { RESULT_CODE, HEADER, DATA, ERROR, MOVED } recv_state;
26         int rc;
27         char *url;
28 };
29
30 static int send_tcp_request(int length, void *buffer, void *ptr) {
31         struct send_recv_state *state = (struct send_recv_state *)ptr;
32
33         if (length > state->send_length - state->bytes_sent)
34                 length = state->send_length - state->bytes_sent;
35         memcpy(buffer, state->send_buffer + state->bytes_sent, length);
36         state->bytes_sent += length;
37         return (length);
38 }
39
40 /**************************************************************************
41 RECV_TCP_CALLBACK - Receive data using TCP
42 **************************************************************************/
43 static int recv_tcp_request(int length, const void *data, void *ptr) {
44         struct send_recv_state *state = (struct send_recv_state *)ptr;
45         const char *buffer = data;
46
47         /* Assume that the lines in an HTTP header do not straddle a packet */
48         /* boundary. This is probably a reasonable assumption */
49         if (state->recv_state == RESULT_CODE) {
50                 while (length > 0) {
51                         /* Find HTTP result code */
52                         if (*buffer == ' ') {
53                                 const char *ptr = buffer + 1;
54                                 int rc = strtoul(ptr, &ptr, 10);
55                                 if (ptr >= buffer + length) {
56                                         state->recv_state = ERROR;
57                                         DBG ( "HTTP got bad result code\n" );
58                                         return 0;
59                                 }
60                                 state->rc = rc;
61                                 state->recv_state = HEADER;
62                                 DBG ( "HTTP got result code %d\n", rc );
63                                 goto header;
64                         }
65                         ++buffer;
66                         length--;
67                 }
68                 state->recv_state = ERROR;
69                 DBG ( "HTTP got no result code\n" );
70                 return 0;
71         }
72         if (state->recv_state == HEADER) {
73         header: while (length > 0) {
74                         /* Check for HTTP redirect */
75                         if (state->rc >= 300 && state->rc < 400 &&
76                             !memcmp(buffer, "Location: ", 10)) {
77                                 char *p;
78                                 
79                                 state->url = p = ( char * ) buffer + 10;
80                                 while ( *p > ' ' ) {
81                                         p++;
82                                 }
83                                 *p = '\0';
84                                 state->recv_state = MOVED;
85                                 DBG ( "HTTP got redirect to %s\n",
86                                       state->url );
87                                 return 1;
88                         }
89                         /* Find beginning of line */
90                         while (length > 0) {
91                                 length--;
92                                 if (*buffer++ == '\n')
93                                         break;
94                         }
95                         /* Check for end of header */
96                         if (length >= 2 && !memcmp(buffer, "\r\n", 2)) {
97                                 state->recv_state = DATA;
98                                 buffer += 2;
99                                 length -= 2;
100                                 break;
101                         }
102                 }
103         }
104         if (state->recv_state == DATA) {
105                 DBG2 ( "HTTP received %d bytes\n", length );
106                 if ( ! fill_buffer ( state->recv_buffer, buffer,
107                                      state->bytes_received, length ) )
108                         return 0;
109                 state->bytes_received += length;
110         }
111         return 1;
112 }
113
114 /**************************************************************************
115 HTTP_GET - Get data using HTTP
116 **************************************************************************/
117 static int http ( char *url, struct sockaddr_in *server __unused,
118                   char *file __unused, struct buffer *buffer ) {
119         struct protocol *proto;
120         struct sockaddr_in http_server = *server;
121         char *filename;
122         static const char GET[] = "GET /%s HTTP/1.0\r\n\r\n";
123         struct send_recv_state state;
124         int length;
125
126         state.rc = -1;
127         state.url = url;
128         state.recv_buffer = buffer;
129         while ( 1 ) {
130                 length = strlen ( filename ) + strlen ( GET );
131                 {
132                         char send_buf[length];
133
134                         sprintf ( send_buf, GET, filename );
135                         state.send_buffer = send_buf;
136                         state.send_length = strlen ( send_buf );
137                         state.bytes_sent = 0;
138                         
139                         state.bytes_received = 0;
140                         state.recv_state = RESULT_CODE;
141                         
142                         tcp_transaction ( server->sin_addr.s_addr,
143                                           server->sin_port, &state,
144                                           send_tcp_request, (int (*)(int, const void *, void *))recv_tcp_request );
145                 }
146
147                 if ( state.recv_state == MOVED ) {
148                         if ( ! parse_url ( state.url, &proto,
149                                            &http_server, &filename ) ) {
150                                 printf ( "Invalid redirect URL %s\n",
151                                          state.url );
152                                 return 0;
153                         }
154                         continue;
155                 }
156                 
157                 break;
158         }
159
160         if ( state.rc != 200 ) {
161                 printf ( "Failed to download %s (rc = %d)\n",
162                          state.url, state.rc );
163                 return 0;
164         }
165
166         return 1;
167 }
168
169 struct protocol http_protocol __protocol = {
170         .name = "http",
171         .default_port = 80,
172         .load = http,
173 };