diff --git a/README.md b/README.md
index a0d58d92b539a07bb3e340900f362daa616ee803..a26efc011c8c450a4e7ba7674a46fb90d25f1637 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,7 @@ https://github.com/tschiemer/aes67
     - [x] [sdp-gen](#sdp-gen): generate SDP
   - RTSP/HTTP
     - [x] [rtsp-describe](#rtsp-describe): retrieve SDP from RTSP service
-    - [ ] rtsp/http combo server?
+    - [ ] ~~rtsp/http combo server?~~ -> [rav-publish](#rav-publish)
   - RAVENNA
     - [ ] ~~RAV2SAP~~ -> [sapd](#sapd)
     - [x] [rav-lookup](#rav-lookup): browse for sessions/devices
@@ -63,16 +63,10 @@ https://github.com/tschiemer/aes67
   - Support
     - mDNS (abstraction for mdns service)
       - [x] dns-sd
-         - [x] discovery
-         - [x] registration
-      - [ ] avahi
-         - [ ] dns-sd compat layer
-    - [ ] RTSP describe
-      - [x] retrieve SDP from server
-      - [ ] serve SDPs  on request
+      - [x] avahi (to be tested further)
+    - [x] RTSP describe client + server
     
-
-
+  
 
 ## In a Nutshell
 
@@ -454,10 +448,15 @@ By default sets up a mini-RTSP server serving the given session descriptions.
 Options:
 	 -h,-?		 Outputs this info
 	 -v		 Some status output to STDERR
-	 -p <rtsp-port>	  Port of RTSP server.
-	 --host <host>	 Host of target device (by default will assume self).
-	 --ip <ip>	 IPv4/6 address of target device (create an record for host).
+	 -p, --port <rtsp-port>	  Port of RTSP server.
+	 --host <host>	 Host of target device (by default will assume self; if given will try to use originator IPv4/6 from SDP file).
+	 --ip <ip>	 (Override) IPv4/6 address of target device (create an record for host).
 	 --no-rtsp	 Do not start a RTSP server.
+	 --http <root>	 Start a http server with given root dir (implies RTSP server)
+	 --http-index <index-file> Index file to serve from directories (default index.html)
+Examples
+./rav-publish -p 9191 --no-rtsp my-session.sdp another-session.sdp
+scripts/html-index.sh sdp.d >> sdp.d/index.html && ./rav-publish -p 9191 --http sdp.d sdp.d/*.sdp
 ```
 
 To quickly register a (test) ravenna session with name `Hello my pretty` on the localhost's port 9191:
diff --git a/scripts/html-index.sh b/scripts/html-index.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6e88cfc4dd25a74226aae1b2740489e19276855d
--- /dev/null
+++ b/scripts/html-index.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+usage(){
+    echo "Usage: $0 <root-directory>"
+    echo "Outputs simple HTML index file starting from <root-directory>"
+}
+
+if [ $# == 0 ]; then
+  usage
+  exit 0;
+fi
+
+ROOT=$1
+
+IFS=$'\n'
+
+i=0
+echo "<!DOCTYPE html><html><body><ul>"
+for filepath in `ls -a "$ROOT" | cat`; do
+  path=`basename "$filepath"`
+  echo "  <li><a href='$path'>$path</a></li>"
+done
+echo "</ul></body></html>"
\ No newline at end of file
diff --git a/src/include/aes67/utils/rtsp-srv.h b/src/include/aes67/utils/rtsp-srv.h
index 96dd235230562f241ff212c2c26888b47ed3d330..4cb1c19a000ef687dfed5c07a2fb95ead52f6936 100644
--- a/src/include/aes67/utils/rtsp-srv.h
+++ b/src/include/aes67/utils/rtsp-srv.h
@@ -61,6 +61,7 @@ enum aes67_rtsp_srv_proto {
 };
 
 enum aes67_rtsp_srv_method {
+    aes67_rtsp_srv_method_undefined = 0,
     aes67_rtsp_srv_method_options   = 1,
     aes67_rtsp_srv_method_describe  = 2,
     aes67_rtsp_srv_method_get       = 4,
@@ -134,6 +135,8 @@ struct aes67_rtsp_srv {
         u16_t sent;
         u16_t len;
         u8_t data[AES67_RTSP_SRV_TXBUFSIZE];
+        void * response_state;
+        bool more;
     } res; // response
 };
 
@@ -148,7 +151,7 @@ void aes67_rtsp_srv_blocking(struct aes67_rtsp_srv * srv, bool blocking);
 void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv);
 
 u16_t aes67_rtsp_srv_sdp_getter(struct aes67_rtsp_srv * srv, void * sdpref, u8_t * buf, u16_t maxlen);
-void aes67_rtsp_srv_http_handler(struct aes67_rtsp_srv * srv, const enum aes67_rtsp_srv_method method, const char * uri, const u8_t urilen, u8_t * buf, u16_t * len, u16_t maxlen, void * response_data);
+void aes67_rtsp_srv_http_handler(struct aes67_rtsp_srv * srv, const enum aes67_rtsp_srv_method method, char * uri, u8_t urilen, u8_t * buf, u16_t * len, u16_t maxlen, bool * more, void ** response_state);
 
 struct aes67_rtsp_srv_resource * aes67_rtsp_srv_sdp_add(struct aes67_rtsp_srv * srv, const char * uri, const u8_t urilen, void * sdpref);
 void aes67_rtsp_srv_sdp_remove(struct aes67_rtsp_srv * srv, void * sdpref);
diff --git a/src/utils/rav-publish/rav-publish.c b/src/utils/rav-publish/rav-publish.c
index bfd3ff4d5bb86ca7f50ba06346aa54180c282a89..59b1b02ecb06172cc7b0b196fb6a82f148abef04 100644
--- a/src/utils/rav-publish/rav-publish.c
+++ b/src/utils/rav-publish/rav-publish.c
@@ -57,6 +57,7 @@ static struct {
     u32_t ttl;
     bool http;
     char * http_root;
+    char * http_index;
 } opts = {
     .verbose = false,
     .rtsp = true,
@@ -65,7 +66,8 @@ static struct {
     .port = 0,
     .ttl = 100,
     .http = false,
-    .http_root = NULL
+    .http_root = NULL,
+    .http_index = "index.html"
 };
 
 static volatile bool keep_running;
@@ -90,7 +92,11 @@ static void help(FILE * fd)
              "\t --ip <ip>\t (Override) IPv4/6 address of target device (create an record for host).\n"
              "\t --no-rtsp\t Do not start a RTSP server.\n"
              "\t --http <root>\t Start a http server with given root dir (implies RTSP server)\n"
-            , argv0);
+             "\t --http-index <index-file> Index file to serve from directories (default index.html)\n"
+             "Examples\n"
+             "%s -p 9191 --no-rtsp my-session.sdp another-session.sdp\n"
+             "scripts/html-index.sh sdp.d >> sdp.d/index.html && %s -p 9191 --http sdp.d sdp.d/*.sdp\n"
+            , argv0, argv0, argv0);
 }
 
 
@@ -255,6 +261,11 @@ static int rtsp_setup()
         return EXIT_FAILURE;
     }
 
+    fprintf(stderr, "Started RTSP%s server on port %hu\n", opts.http ? "/HTTP" : "", opts.port);
+    if (opts.http){
+        fprintf(stderr, "HTTP root = %s\n", opts.http_root);
+    }
+
     return EXIT_SUCCESS;
 }
 
@@ -295,13 +306,176 @@ u16_t aes67_rtsp_srv_sdp_getter(struct aes67_rtsp_srv * srv, void * sdpref, u8_t
     return len + sdpres->len;
 }
 
+void aes67_rtsp_srv_http_handler(struct aes67_rtsp_srv * srv, const enum aes67_rtsp_srv_method method, char * uri, u8_t urilen, u8_t * buf, u16_t * len, u16_t maxlen, bool * more, void ** response_state)
+{
+    static struct {
+        int fd;
+        off_t filesize;
+        off_t read;
+    } state = {
+        .fd = -1
+    };
+
+    // if data is set, this is from a previous call but the file to be transmitted was to big for the internal buffer to send at once.
+    // possibly the
+    if (*response_state){
+
+        // premature termination
+        if (state.fd != -1){
+            close(state.fd);
+            return;
+        }
+
+        // continue passing file contents to server
+
+        ssize_t r = read(state.fd, buf + *len, maxlen);
+
+        if (r > 0){
+            *len = r;
+            state.read += r;
+        }
+
+        *more = state.read < state.filesize;
+
+        if (!*more){
+            close(state.fd);
+            state.fd = -1;
+        }
+
+        return;
+    }
+
+
+    uri[urilen] = '\0';
+//    fprintf(stderr, "HTTP request for [%s]\n", uri);
+
+    char uri_decoded[256];
+
+    uri_decode(uri, urilen, uri_decoded);
+
+
+    // basic safety check
+    if (strstr(uri_decoded, "..") != NULL){
+        printf("404 1\n");
+        *len = snprintf((char*)buf, maxlen,
+                        "HTTP/1.1 404 Not Found\r\n"
+                        "Connection: close\r\n"
+                        "\r\n"
+        );
+        return;
+    }
+
+//    u16_t l = 0;
+
+//    if (urilen > 4 && memcmp(uri, "/api", 4) == 0){
+//
+//        // this is mor a proof of concept when trying to implement a restful api
+//        *len = snprintf((char*)buf, maxlen,
+//                        "HTTP/1.1 200 OK\r\n"
+//                        "Connection: close\r\n"
+//                        "Content-Type: application/json\r\n"
+//                        "\r\n"
+//                        "{code: 200, msg: \"proof of concept\"}"
+//        );
+//        return;
+//
+//    }
+
+    char path[256];
+
+    snprintf(path, sizeof(path), "%s%s", opts.http_root, uri_decoded);
+
+//    printf("file = [%s]\n", path);
+
+    // check file existence
+    if (access(path, F_OK) != 0){
+        fprintf(stderr, "ERROR 404 2\n");
+        *len = snprintf((char*)buf, maxlen,
+                 "HTTP/1.1 404 Not Found\r\n"
+                 "Connection: close\r\n"
+                 "\r\n"
+         );
+        return;
+    }
+
+    struct stat st;
+
+    //get file stat
+    if (stat(path, &st)){
+        fprintf(stderr, "ERROR 500 1\n");
+        *len = snprintf((char*)buf, maxlen,
+                        "HTTP/1.1 500 Internal Server Error\r\n"
+                        "Connection: close\r\n"
+                        "\r\n"
+        );
+        return;
+    }
+
+    // forbid directories (no listing)
+    if ((st.st_mode & S_IFMT) == S_IFDIR){
+        // check if there is an index file
+
+        snprintf(path, sizeof(path), "%s%s/%s", opts.http_root, uri, opts.http_index);
+
+        if (access(path, F_OK) || stat(path, &st) || (st.st_mode & S_IFMT) != S_IFREG){
+
+            fprintf(stderr, "ERROR 403 1\n");
+            *len = snprintf((char*)buf, maxlen,
+                            "HTTP/1.1 403 Forbidden\r\n"
+                            "Connection: close\r\n"
+                            "\r\n"
+            );
+            return;
+        }
+    }
+
+    state.fd = open(path, O_RDONLY);
+    if (state.fd == -1){
+        fprintf(stderr, "ERROR 500 2 \n");
+        *len = snprintf((char*)buf, maxlen,
+                        "HTTP/1.1 500 Internal Server Error\r\n"
+                        "Connection: close\r\n"
+                        "\r\n"
+        );
+        return;
+    }
+
+    state.filesize = st.st_size;
+
+    *len = snprintf((char*)buf, maxlen,
+                    "HTTP/1.1 200 OK\r\n"
+                    "Connection: close\r\n"
+                    "Content-Length: %lld\r\n"
+                    "\r\n"
+                    , st.st_size
+    );
+
+    state.read = 0;
+
+    ssize_t remaining = maxlen - *len;
+    ssize_t r = read(state.fd, buf + *len, remaining);
+
+    if (r > 0){
+        *len += r;
+        state.read += r;
+    }
+
+    *more = state.read < state.filesize;
+
+    if (!*more){
+        close(state.fd);
+        state.fd = -1;
+    }
+
+    *response_state = &state;
+}
+
 static int load_sdpres(char * fname, size_t maxlen)
 {
     if (access(fname, F_OK) != 0){
         fprintf(stderr, "ERROR file exists? %s\n", fname);
         return EXIT_FAILURE;
     }
-
     struct stat st;
 
     if (stat(fname, &st)){
@@ -402,6 +576,7 @@ int main(int argc, char * argv[])
                 {"no-rtsp",  no_argument,       0,  3 },
                 {"ttl", required_argument, 0, 4},
                 {"http", required_argument, 0, 5},
+                {"http-index", required_argument, 0, 6},
                 {0,         0,                 0,  0 }
         };
 
@@ -449,21 +624,31 @@ int main(int argc, char * argv[])
                 }
                 break;
 
-            case 5:
-                if (access(optarg, F_OK) != 0){
+            case 5: {
+                if (access(optarg, F_OK) != 0) {
                     fprintf(stderr, "ERROR http root does not exists? %s\n", optarg);
                     return EXIT_FAILURE;
                 }
+
+                struct stat st;
+
+                if (stat(optarg, &st)){
+                    fprintf(stderr, "ERROR failed to get filesize %s\n", optarg);
+                    return EXIT_FAILURE;
+                }
+                if ((st.st_mode & S_IFMT) != S_IFDIR){
+                    fprintf(stderr, "ERROR http root is not a directory: %s\n", optarg);
+                    return EXIT_FAILURE;
+                }
+
                 opts.http = true;
                 opts.http_root = optarg;
 
+                break;
+            }
 
-//                struct stat st;
-
-//                if (stat(fname, &st)){
-//                    fprintf(stderr, "ERROR failed to get filesize %s\n", fname);
-//                    return EXIT_FAILURE;
-//                }
+            case 6:
+                opts.http_index = optarg;
                 break;
 
             case '?':
diff --git a/src/utils/rtsp-srv.c b/src/utils/rtsp-srv.c
index 33c68d86944ee4bffd1f30949fad1f0c426bb495..291778c45aec283278f2260ae50111738fcd1b3a 100644
--- a/src/utils/rtsp-srv.c
+++ b/src/utils/rtsp-srv.c
@@ -244,7 +244,7 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv)
                     if (c == '\n'){
 
                         // basic sanity check
-                        if (srv->req.data_len < 32){
+                        if (srv->req.data_len < 15){
                             close(srv->client_sockfd);
                             srv->client_sockfd = -1;
                             srv->state = aes67_rtsp_srv_state_listening;
@@ -257,6 +257,8 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv)
 
                         u8_t * s = &srv->req.data[srv->req.data_len - CR - sizeof("HTTP/1.0")];
 
+//                        printf("[%s] [%s]\n", srv->req.data, s);
+
                         if (s[0] == 'H' &&
                             s[1] == 'T' &&
                             s[2] == 'T' &&
@@ -264,7 +266,6 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv)
                             s[4] == '/' &&
                             s[6] == '.'
                         ){
-
                             if (!srv->http_enabled){
                                 close(srv->client_sockfd);
                                 srv->client_sockfd = -1;
@@ -297,7 +298,9 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv)
                         srv->req.version.minor = s[7] - '0';
 
                         s = srv->req.data;
-                        fprintf(stderr, "[%s]\n", s);
+
+                        srv->req.data[srv->req.data_len] = '\0';
+                        fprintf(stderr, "REQUEST %s", s);
 
                         if (srv->req.proto == aes67_rtsp_srv_proto_rtsp &&
                             s[0] == 'D' &&
@@ -372,7 +375,7 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv)
                         srv->req.urilen = &srv->req.data[srv->req.data_len - CR - sizeof(" HTTP/1.0")] - srv->req.uri;
 
                         srv->req.uri[srv->req.urilen] = '\0';
-                        printf("uri[%d] = [%s]\n", srv->req.urilen, srv->req.uri);
+//                        printf("uri[%d] = [%s]\n", srv->req.urilen, srv->req.uri);
                         // if  rtsp, discard scheme and host
                         if (srv->req.proto == aes67_rtsp_srv_proto_rtsp){
 
@@ -404,7 +407,16 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv)
                             srv->req.urilen -= delim - srv->req.uri;
                             srv->req.uri = delim;
                         }
-                        printf("uri[%d] = [%s]\n", srv->req.urilen, srv->req.uri);
+                        else if (srv->req.proto == aes67_rtsp_srv_proto_http){
+                            // sanity check
+                            if (srv->req.uri[0] != '/'){
+                                close(srv->client_sockfd);
+                                srv->client_sockfd = -1;
+                                srv->state = aes67_rtsp_srv_state_listening;
+                                return;
+                            }
+                        }
+//                        printf("uri[%d] = [%s]\n", srv->req.urilen, srv->req.uri);
 
                         // set line start
                         srv->req.line = &srv->req.data[srv->req.data_len];
@@ -437,7 +449,7 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv)
                     close(srv->client_sockfd);
                     srv->client_sockfd = -1;
                     srv->state = aes67_rtsp_srv_state_listening;
-                    printf("closed!\n");
+//                    printf("closed!\n");
                     return;
                 } else if (r == 1) {
                     srv->req.data[srv->req.data_len++] = c;
@@ -447,7 +459,7 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv)
                     if (c == '\n') {
 
                         // end of header? ([CR]NL)
-                        if (srv->req.llen == 1 + CR && (!CR || srv->req.line[0] == '\r') && srv->req.line[1] == '\n') {
+                        if (srv->req.llen == 1 + CR && (!CR || srv->req.line[0] == '\r') && srv->req.line[CR] == '\n') {
                             srv->req.header_len = srv->req.data_len;
                             // break to start reading body
                             break;
@@ -549,7 +561,7 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv)
 
     if (srv->state == aes67_rtsp_srv_state_processing){
         if (srv->req.proto == aes67_rtsp_srv_proto_rtsp){
-            fprintf(stderr, "is rtsp\n");
+//            fprintf(stderr, "is rtsp\n");
 
             u16_t status_code = AES67_RTSP_STATUS_INTERNAL_ERROR;
             struct aes67_rtsp_srv_resource * res = NULL;
@@ -557,7 +569,6 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv)
             switch (srv->req.method){
 
                 case aes67_rtsp_srv_method_options:
-                    fprintf(stderr, "method = options\n");
                     status_code = AES67_RTSP_STATUS_OK;
                     break;
 
@@ -674,22 +685,75 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv)
 
         } // proto == aes67_rtsp_srv_proto_rtsp
         else if (srv->req.proto == aes67_rtsp_srv_proto_http){
-            fprintf(stderr, "is http\n");
+
+            srv->res.more = false;
+            srv->res.len = 0;
+            srv->res.sent = 0;
+            srv->res.response_state = NULL;
+
+            // NULL-terminate just to make simpler for processor (note, we're altering the buffer hereby, obviously..)
+//            srv->req.uri[srv->req.urilen] = '\0';
+
+            aes67_rtsp_srv_http_handler(srv, srv->req.method, (char*)srv->req.uri, srv->req.urilen, srv->res.data, &srv->res.len, AES67_RTSP_SRV_TXBUFSIZE, &srv->res.more, &srv->res.response_state);
+
+            if (srv->res.len == 0){
+//                fprintf(stderr, "no http data, closing\n");
+
+                close(srv->client_sockfd);
+                srv->client_sockfd = -1;
+                srv->state = aes67_rtsp_srv_state_listening;
+            } else {
+                srv->state = aes67_rtsp_srv_state_sending;
+            }
+
         } // proto == aes67_rtsp_srv_proto_http
     } // state == aes67_rtsp_srv_state_processing
 
     if (srv->state == aes67_rtsp_srv_state_sending){
 
         if (srv->res.sent < srv->res.len){
-            fprintf(stderr, "Sending %d bytes..\n", srv->res.len - srv->res.sent);
-            write(srv->client_sockfd, srv->res.data + srv->res.sent, srv->res.len - srv->res.sent);
+//            fprintf(stderr, "Sending %d bytes..\n", srv->res.len - srv->res.sent);
+
+            int r = write(srv->client_sockfd, srv->res.data + srv->res.sent, srv->res.len - srv->res.sent);
+
+            if (r > 0){
+                srv->res.sent += r;
 
-            srv->res.sent = srv->res.len;
+                // just some sanity check because I'm not sure how this behaves in real life
+                assert(r == srv->res.len);
+
+                srv->res.len = 0;
+
+                // if http with more data to send, call handler again
+                if (srv->req.proto == aes67_rtsp_srv_proto_http && srv->res.more) {
+                    aes67_rtsp_srv_http_handler(srv, srv->req.method, (char *) srv->req.uri, srv->req.urilen, srv->res.data,
+                                                &srv->res.len, AES67_RTSP_SRV_TXBUFSIZE, &srv->res.more,
+                                                &srv->res.response_state);
+                }
+
+                // no more data, terminate
+                if (srv->res.len == 0){
+
+                    close(srv->client_sockfd);
+                    srv->client_sockfd = -1;
+
+                    srv->state = aes67_rtsp_srv_state_listening;
+                }
+            }
+            // if socket closed
+            else if (r == 0){
+
+                if (srv->req.proto == aes67_rtsp_srv_proto_http){
+                    aes67_rtsp_srv_http_handler(srv, aes67_rtsp_srv_method_undefined, NULL, 0, NULL, NULL, 0, &srv->res.more, &srv->res.response_state);
+                }
+
+                close(srv->client_sockfd);
+                srv->client_sockfd = -1;
+
+                srv->state = aes67_rtsp_srv_state_listening;
+            }
 
-            srv->state = aes67_rtsp_srv_state_listening;
 
-            close(srv->client_sockfd);
-            srv->client_sockfd = -1;
         }
 
     } //state == aes67_rtsp_srv_state_sending
@@ -698,13 +762,11 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv)
 WEAK_FUN u16_t aes67_rtsp_srv_sdp_getter(struct aes67_rtsp_srv * srv, void * sdpref, u8_t * buf, u16_t maxlen)
 {
     assert(false);
-
-    return AES67_RTSP_STATUS_INTERNAL_ERROR;
 }
 
-WEAK_FUN void aes67_rtsp_srv_http_handler(struct aes67_rtsp_srv * srv, const enum aes67_rtsp_srv_method method, const char * uri, const u8_t urilen, u8_t * buf, u16_t * len, u16_t maxlen, void * response_data)
+WEAK_FUN void aes67_rtsp_srv_http_handler(struct aes67_rtsp_srv * srv, const enum aes67_rtsp_srv_method method, char * uri, u8_t urilen, u8_t * buf, u16_t * len, u16_t maxlen, bool * more, void ** response_state)
 {
-
+    assert(false);
 }