--- /dev/null
+#include <stddef.h>
+#include <string.h>
+#include <vsprintf.h>
+#include <assert.h>
+#include <gpxe/async.h>
+#include <gpxe/http.h>
+
+/** @file
+ *
+ * The Hyper Text Transfer Protocol (HTTP)
+ *
+ * This file implements the TCP-based HTTP protocol. It connects to the
+ * server specified in http_request::tcp and transmit an HTTP GET request
+ * for the file specified in http_request::filename. It then decoded the
+ * HTTP header, determining file status and file size. Then sends the file
+ * to the callback function at http_request::callback().
+ * **NOTE**: still working on correcting the closing of the tcp connection
+ *
+ * To use this code, do something like:
+ *
+ * @code
+ *
+ * static void my_callback ( struct http_request *http, char *data, size_t len ) {
+ * ... process data ...
+ * }
+ *
+ * struct http_request http = {
+ * .filename = "path/to/file",
+ * .callback = my_callback,
+ * };
+ *
+ * ... assign http.tcp.server ...
+ *
+ * rc = async_wait ( get_http ( &http ) );
+ *
+ * @endcode
+ *
+ */
+
+static inline struct http_request *
+tcp_to_http ( struct tcp_connection *conn ) {
+ return container_of ( conn, struct http_request, tcp );
+}
+
+/**
+ * Close an HTTP connection
+ *
+ * @v conn a TCP Connection
+ * @v status connection status at close
+ */
+static void http_closed ( struct tcp_connection *conn, int status ) {
+ struct http_request *http = tcp_to_http ( conn );
+ async_done ( &http->aop, status );
+}
+
+/**
+ * Callback after a TCP connection is established
+ *
+ * @v conn a TCP Connection
+ */
+static void http_connected ( struct tcp_connection *conn ) {
+ struct http_request *http = tcp_to_http ( conn );
+
+ http->state = HTTP_REQUEST_FILE;
+}
+
+/**
+ * Callback for when TCP data is acknowledged
+ *
+ * @v conn a TCP Connection
+ * @v len the length of data acked
+ */
+static void http_acked ( struct tcp_connection *conn, size_t len ) {
+ struct http_request *http = tcp_to_http ( conn );
+
+ // assume that the whole GET request was sent in on epacket
+
+ switch ( http->state ) {
+ case HTTP_REQUEST_FILE:
+ http->state = HTTP_PARSE_HEADER;
+ break;
+ case HTTP_PARSE_HEADER:
+ case HTTP_RECV_FILE:
+ break;
+ case HTTP_DONE:
+ //tcp_close(conn);
+ break;
+ default:
+ break;
+ }
+ //printf("acked\n");
+}
+
+/**
+ * Callback when new TCP data is recieved
+ *
+ * @v conn a TCP Connection
+ * @v data a pointer to the data recieved
+ * @v len length of data buffer
+ */
+static void http_newdata ( struct tcp_connection *conn, void *data,
+ size_t len ) {
+ struct http_request *http = tcp_to_http ( conn );
+ char *content_length;
+ char *start = data;
+ char *rcp; int rc;
+
+ switch ( http->state ) {
+ case HTTP_PARSE_HEADER:
+ if(strncmp("HTTP/",data,5) != 0){
+ // no http header
+ printf("Error: no HTTP Header\n");
+ }
+ // if rc is not 200, then handle problem
+ // either redirect or not there
+ rcp = strstr(data,"HTTP");
+ if(rcp == NULL){ printf("Could not find header status line.\n"); }
+ rcp += 9;
+ rc = strtoul(rcp,NULL,10);
+ printf("RC=%d\n",rc);
+ content_length = strstr(data,"Content-Length: ");
+ if(content_length != NULL){
+ content_length += 16;
+ http->file_size = strtoul(content_length,NULL,10);
+ http->file_recv = 0;
+ printf("http->file_size = %d\n", http->file_size);
+ }
+ start = strstr(data,"\r\n\r\n");
+ if(start == NULL){ printf("No end of header\n"); }
+ else{
+ start += 4;
+ len -= ((void *)start - data);
+ http->state = HTTP_RECV_FILE;
+ }
+
+ if ( http->state != HTTP_RECV_FILE )
+ break;
+ case HTTP_RECV_FILE:
+ http->callback(http,start,len);
+ //http->file_size -= len;
+ //printf("File recv is %d\n", http->file_recv);
+ if ( http->file_recv == http->file_size ){
+ http->state = HTTP_DONE;
+ tcp_close(conn);
+ }
+ break;
+ case HTTP_REQUEST_FILE:
+ case HTTP_DONE:
+ default:
+ break;
+ }
+}
+
+/**
+ * Callback for sending TCP data
+ *
+ * @v conn a TCP Connection
+ */
+static void http_senddata ( struct tcp_connection *conn, void *buf, size_t len ) {
+ struct http_request *http = tcp_to_http ( conn );
+ char buf[66]; // 16 request + 50 for filename
+ size_t len;
+
+ switch ( http->state ){
+ case HTTP_REQUEST_FILE:
+ len = snprintf(buf,66,"GET %s HTTP/1.0\r\n\r\n",http->filename);
+ printf("%s\n",buf);
+ // string is: GET <file> HTTP/1.0\r\n\r\n
+
+ tcp_send ( conn, buf, len);
+ break;
+ case HTTP_PARSE_HEADER:
+ case HTTP_RECV_FILE:
+ break;
+ case HTTP_DONE:
+ //tcp_close(conn)
+ break;
+ default:
+ break;
+ }
+}
+
+static struct tcp_operations http_tcp_operations = {
+ .closed = http_closed,
+ .connected = http_connected,
+ .acked = http_acked,
+ .newdata = http_newdata,
+ .senddata = http_senddata,
+};
+
+/**
+ * Initiate a HTTP connection
+ *
+ * @v http a HTTP request
+ */
+struct async_operation * get_http ( struct http_request *http ) {
+ http->tcp.tcp_op = &http_tcp_operations;
+ http->state = HTTP_REQUEST_FILE;
+ tcp_connect ( &http->tcp );
+ return &http->aop;
+}
return 0;
}
+static int test_dhcp_http ( struct net_device *netdev, char *url ) {
+ union {
+ struct sockaddr_in sin;
+ struct sockaddr_tcpip st;
+ } target;
+
+ memset ( &target, 0, sizeof ( target ) );
+ target.sin.sin_family = AF_INET;
+ target.sin.sin_port = htons ( 80 );
+
+ char *addr = url + 7; // http://
+ char *file = strchr(addr, '/');
+ *file = '\0'; // for printf and inet_aton to work
+ printf("connecting to %s\n", addr);
+ inet_aton ( addr, &target.sin.sin_addr );
+ *file = '/';
+ test_http ( netdev, &target.st, file );
+ return 0;
+}
+
static int test_dhcp_tftp ( struct net_device *netdev, char *tftpname ) {
union {
struct sockaddr_in sin;
return test_dhcp_iscsi_boot ( &filename[6] );
} else if ( strncmp ( filename, "hello:", 6 ) == 0 ) {
return test_dhcp_hello ( &filename[6] );
+ } else if ( strncmp ( filename, "http:", 5 ) == 0 ) {
+ return test_dhcp_http ( netdev, filename );
} else {
return test_dhcp_tftp ( netdev, filename );
}
--- /dev/null
+#include <stdint.h>
+#include <string.h>
+#include <byteswap.h>
+#include <console.h>
+#include <vsprintf.h>
+#include <gpxe/async.h>
+#include <gpxe/http.h>
+#include <gpxe/ip.h>
+#include <gpxe/uaccess.h>
+#include "pxe.h"
+
+static void test_http_callback ( struct http_request *http, char *data, size_t len ) {
+ userptr_t pxe_buffer = real_to_user ( 0, 0x7c00 );
+ unsigned long offset = http->file_recv;
+ http->file_recv += len;
+ copy_to_user ( pxe_buffer, offset, data, len );
+}
+
+void test_http ( struct net_device *netdev, struct sockaddr_tcpip *server, const char *filename ) {
+ struct http_request http;
+ int rc;
+
+ memset ( &http, 0, sizeof ( http ) );
+ memcpy ( &http.tcp.peer, server, sizeof ( http.tcp.peer ) );
+ http.filename = filename;
+ http.callback = test_http_callback;
+
+ rc = async_wait ( get_http ( &http ) );
+ if ( rc ) {
+ printf ( "HTTP fetch failed\n" );
+ }
+
+ printf ( "Attempting PXE boot\n" );
+ pxe_netdev = netdev;
+ rc = pxe_boot();
+ printf ( "PXE NBP returned with status %04x\n", rc);
+}