in progress
authorVictor Grishchenko <victor.grishchenko@gmail.com>
Tue, 9 Mar 2010 08:00:00 +0000 (09:00 +0100)
committerVictor Grishchenko <victor.grishchenko@gmail.com>
Tue, 9 Mar 2010 08:00:00 +0000 (09:00 +0100)
README
httpgw.cpp

diff --git a/README b/README
index 0329cc1..8939346 100644 (file)
--- a/README
+++ b/README
@@ -1,9 +1,24 @@
 swift: the multiparty transport protocol
+    (aka BitTorrent at the transport layer)
+    Differently from TCP, the protocol does not use the ordered data stream
+    abstraction. Effectively, it splits a file into 1KB packets and sends
+    them around. The secret sauce is Merkle hash trees and binmaps.
 
 see doc/index.html for marketing stuff, ideas and rants
     doc/swift.txt for protocol draft spec
     *.cpp for the actual code
     swift.cpp is the main exec file; may run as e.g.
+    
         ./swift -t node300.das2.ewi.tudelft.nl:20000 -h \
         d1502706c46779d361a1d562a10da0a45c4c40e5 -f \
         trailer.ogg
+        
+    ...to retrieve video and save it to a file, or...
+
+        ./swift -t mfold.libswift.org:20000 -h \
+        d1502706c46779d361a1d562a10da0a45c4c40e5 -r . \
+        -h 8080
+        
+    ...and then point your browser at
+    
+        http://localhost:8080/d1502706c46779d361a1d562a10da0a45c4c40e5
index bb03be3..af99412 100644 (file)
 #include "swift.h"
 
-#define MAX_HTTP_CLIENT 128
+#define HTTPGW_MAX_CLIENT 128
+enum {
+    HTTPGW_RANGE,
+    HTTPGW_MAX_HEADER
+};
+
 
 struct http_gw_t {
     uint64_t offset;
     uint64_t tosend;
     int      transfer;
     SOCKET   sink;
-} http_clients[MAX_HTTP_CLIENT];
+    char*    headers[HTTP_MAX_HEADER];
+} http_requests[HTTP_MAX_CLIENT];
+
+
+int http_gw_reqs_open = 0;
 
-void HttpGwErrorCallback (SOCKET sink) {
 
+http_gw_t* HttpGwFindRequest (SOCKET sock) {
+    for(int i=0; i<http_gw_reqs_open; i++)
+        if (http_requests[i].sink==sock)
+            return http_requests[i];
+    return NULL;
 }
 
+
+void HttpGwCloseConnection (SOCKET sock) {
+    http_gw_t* req = HttpGwFindRequest(sock);
+    if (req) {
+        for(int i=0; i<HTTPGW_MAX_HEADER; i++)
+            if (req->headers[i]) {
+                free(req->headers[i]);
+                req->headers[i] = NULL;
+            }
+        *req = http_requests[http_gw_reqs_open--];
+    }
+    close_socket(sock);
+}
+
 void HttpGwMayWriteCallback (SOCKET sink) {
-    // if have data => write
-    // otherwise, change mask
-    if (not_enough_data)
-        swift::Listen3rdPartySocket(http_conns[httpc].sink,NULL,NULL,ErrorCallback);
-    if (all_done)
-        swift::Listen3rdPartySocket(http_conns[httpc].sink,NewRequestCallback,NULL,ErrorCallback);
+    http_gw_t* req = HttpGwFindRequest(sink);
+    uint64_t complete = swift::SeqComplete(http_requests[reqi].transfer);
+    if (complete>req->offset) { // send data
+        char buf[1LL<<12];
+        uint64_t tosend = std::min(1LL<<12,complete-req->offset);
+        size_t rd = read(req->transfer,buf,tosend); // hope it is cached
+        if (rd<0) {
+            HttpGwCloseConnection(sink);
+            return;
+        }
+        size_t wn = send(sink, buf, rd, 0);
+        if (wn<0) {
+            HttpGwCloseConnection(sink);
+            return;
+        }
+        req->offset += wn;
+        req->tosend -= wn;
+    } else {
+        if (swift::IsComplete(http_requests[reqi].transfer))  // done; wait for new request
+            swift::Listen3rdPartySocket
+                (http_conns[httpc].sink,NewRequestCallback,NULL,ErrorCallback);
+        else  // wait for data
+            swift::Listen3rdPartySocket(request.sink,NULL,NULL,ErrorCallback);
+    }
 }
 
 
 void SwiftProgressCallback (int transfer, bin64_t bin) {
     for (int httpc=0; httpc<conn_count; httpc++)
         if (http_conns[httpc].transfer==transfer) {
-            // check mask
-            if (bin==http_conns[httpc].offset)
-                Listen3rdPartySocket(http_conns[httpc].sink,NULL,MayWriteCallback,ErrorCallback);
+            if (bin.offset()<<10==http_conns[httpc].offset)
+                swift::Listen3rdPartySocket
+                (http_conns[httpc].sink,NULL,MayWriteCallback,ErrorCallback);
         }
 }
 
 
-
 void HttpGwNewRequestCallback (SOCKET http_conn){
     // read headers - the thrilling part
-    // we surely do not support pipelining => one requests at a time
-    fgets();
+    // we surely do not support pipelining => one request at a time
+    #define HTTPGW_MAX_REQ_SIZE 1024
+    char buf[HTTPGW_MAX_REQ_SIZE+1];
+    int rd = recv(http_conn,buf,HTTPGW_MAX_REQ_SIZE,0);
+    if (rd<=0) { // if conn is closed by the peer, rd==0
+        HttpGwCloseRequest(http_conn);
+        return;
+    }
+    buf[rd] = 0;
     // HTTP request line
-    sscanf();
+    char* reqline = strtok(buf,"\r\n");
+    char method[16], url[512], version[16], crlf[5];
+    if (4!=sscanf(reqline,"%16s %512s %16s%4[\n\r]",method,url,version,crlf)) {
+        HttpGwCloseRequest(http_conn);
+        return;
+    }
     // HTTP header fields
-    sscanf();
-    // incomplete header => screw it
-    fprintf("400 Incomplete header\r\n");
-    close();
+    char* headerline;
+    while (headerline=strtok(NULL,"\n\r")) {
+        char header[128], value[256];
+        if (3!=sscanf(headerline,"%120[^: \r\n]: %250[^\r\n]%4[\r\n]",header,value,crlf)) {
+            HttpGwCloseRequest(http_conn);
+            return;
+        }
+        for(int i=0; i<HTTPGW_HEADER_COUNT; i++)
+            if (0==strcasecmp(HTTPGW_HEADERS[i],header) && !http_requests[reqi])
+                http_requests[reqi] = strdup(value);
+    }
     // initiate transmission
+    // parse URL
+    // find/create transfer
+    SwiftProgressCallback;
     // write response header
+    sprintf("200 OK\r\n");
+    sprintf("Header: value\r\n");
     http_clients[i].offset = 0;
     http_clients[i].tosend = 10000;
     http_clients[i].transfer = file;
@@ -57,14 +126,16 @@ void HttpGwNewRequestCallback (SOCKET http_conn){
 // be liberal in what you do, be conservative in what you accept
 void HttpGwNewConnectionCallback (SOCKET serv) {
     Address client_address;
-    SOCKET conn = accept (serv, & (client_address.addr), sizeof(struct sockaddr_in));
+    SOCKET conn = accept 
+        (serv, & (client_address.addr), sizeof(struct sockaddr_in));
     if (conn==INVALID_SOCKET) {
         print_error("client conn fails");
         return;
     }
     make_socket_nonblocking(conn);
     // submit 3rd party socket to the swift loop
-    socket_callbacks_t install(conn,HttpGwNewRequestCallback,HttpGwMayWriteCallback,HttpGwErrorCallback);
+    socket_callbacks_t install
+        (conn,HttpGwNewRequestCallback,HttpGwMayWriteCallback,HttpGwErrorCallback);
     swift::Listen3rdPartySocket(install);
 }
 
@@ -76,7 +147,9 @@ void HttpGwError (SOCKET serv) {
 
 SOCKET InstallHTTPGateway (Address bind_to) {
     SOCKET fd;
-    #define gw_ensure(x) { if (!(x)) { print_error("http binding fails"); close_socket(fd); return INVALID_SOCKET; } }
+    #define gw_ensure(x) { if (!(x)) { \
+    print_error("http binding fails"); close_socket(fd); \
+    return INVALID_SOCKET; } }
     gw_ensure ( (fd=socket(AF_INET, SOCK_STREAM, 0)) != INVALID_SOCKET );
     gw_ensure ( 0==bind(fd, (sockaddr*)&(bind_to.addr), sizeof(struct sockaddr_in)) );
     gw_ensure (make_socket_nonblocking(fd));