diff --git a/lib/http.c b/lib/http.c
index 9ce216ef4174a88081833d96750d317999f55fa6..4ec38735a87be63476f1d5397dc9d5fb5bcb05e3 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -1493,6 +1493,10 @@ static CURLcode expect100(struct SessionHandle *data,
   const char *ptr;
   data->state.expect100header = FALSE; /* default to false unless it is set
                                           to TRUE below */
+  if(conn->httpversion == 20) {
+    /* We don't use Expect in HTTP2 */
+    return CURLE_OK;
+  }
   if(use_http_1_1plus(data, conn)) {
     /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
        100-continue to the headers which actually speeds up post operations
@@ -1797,35 +1801,40 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
   }
 #endif
 
-  ptr = Curl_checkheaders(data, "Transfer-Encoding:");
-  if(ptr) {
-    /* Some kind of TE is requested, check if 'chunked' is chosen */
-    data->req.upload_chunky =
-      Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
-  }
+  if(conn->httpversion == 20)
+    /* In HTTP2 forbids Transfer-Encoding: chunked */
+    ptr = NULL;
   else {
-    if((conn->handler->protocol&CURLPROTO_HTTP) &&
-       data->set.upload &&
-       (data->set.infilesize == -1)) {
-      if(conn->bits.authneg)
-        /* don't enable chunked during auth neg */
-        ;
-      else if(use_http_1_1plus(data, conn)) {
-        /* HTTP, upload, unknown file size and not HTTP 1.0 */
-        data->req.upload_chunky = TRUE;
+    ptr = Curl_checkheaders(data, "Transfer-Encoding:");
+    if(ptr) {
+      /* Some kind of TE is requested, check if 'chunked' is chosen */
+      data->req.upload_chunky =
+        Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
+    }
+    else {
+      if((conn->handler->protocol&CURLPROTO_HTTP) &&
+         data->set.upload &&
+         (data->set.infilesize == -1)) {
+        if(conn->bits.authneg)
+          /* don't enable chunked during auth neg */
+          ;
+        else if(use_http_1_1plus(data, conn)) {
+          /* HTTP, upload, unknown file size and not HTTP 1.0 */
+          data->req.upload_chunky = TRUE;
+        }
+        else {
+          failf(data, "Chunky upload is not supported by HTTP 1.0");
+          return CURLE_UPLOAD_FAILED;
+        }
       }
       else {
-        failf(data, "Chunky upload is not supported by HTTP 1.0");
-        return CURLE_UPLOAD_FAILED;
+        /* else, no chunky upload */
+        data->req.upload_chunky = FALSE;
       }
-    }
-    else {
-      /* else, no chunky upload */
-      data->req.upload_chunky = FALSE;
-    }
 
-    if(data->req.upload_chunky)
-      te = "Transfer-Encoding: chunked\r\n";
+      if(data->req.upload_chunky)
+        te = "Transfer-Encoding: chunked\r\n";
+    }
   }
 
   Curl_safefree(conn->allocptr.host);
@@ -2465,7 +2474,10 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
 
     if(data->set.postfields) {
 
-      if(!data->state.expect100header &&
+      /* In HTTP2, we send request body in DATA frame regardless of
+         its size. */
+      if(conn->httpversion != 20 &&
+         !data->state.expect100header &&
          (postsize < MAX_INITIAL_POST_SIZE))  {
         /* if we don't use expect: 100  AND
            postsize is less than MAX_INITIAL_POST_SIZE
diff --git a/lib/http.h b/lib/http.h
index db322bf96b26dfcc03946bb4bf1dce41fb850605..407f7b62159c9895a98f1e3fd5491353071c5aca 100644
--- a/lib/http.h
+++ b/lib/http.h
@@ -158,7 +158,7 @@ struct http_conn {
   nghttp2_session *h2;
   uint8_t binsettings[H2_BINSETTINGS_LEN];
   size_t  binlen; /* length of the binsettings data */
-  char *mem;     /* points to a buffer in memory to store or read from */
+  char *mem;     /* points to a buffer in memory to store */
   size_t len;    /* size of the buffer 'mem' points to */
   bool bodystarted;
   sending send_underlying; /* underlying send Curl_send callback */
@@ -172,6 +172,14 @@ struct http_conn {
                           on_data_chunk */
   size_t datalen; /* the number of bytes left in data */
   char *inbuf; /* buffer to receive data from underlying socket */
+  /* We need separate buffer for transmission and reception because we
+     may call nghttp2_session_send() after the
+     nghttp2_session_mem_recv() but mem buffer is still not full. In
+     this case, we wrongly sends the content of mem buffer if we share
+     them for both cases. */
+  const uint8_t *upload_mem; /* points to a buffer to read from */
+  size_t upload_len; /* size of the buffer 'upload_mem' points to */
+  size_t upload_left; /* number of bytes left to upload */
 #else
   int unused; /* prevent a compiler warning */
 #endif
diff --git a/lib/http2.c b/lib/http2.c
index a4baca9f120decec25a863e7618203a0243e9af2..e8d31d5ca8ebae9e08b3e71d9b6af4baf66e9d60 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -34,6 +34,7 @@
 #include "curl_base64.h"
 #include "curl_memory.h"
 #include "rawstr.h"
+#include "multiif.h"
 
 /* include memdebug.h last */
 #include "memdebug.h"
@@ -42,6 +43,38 @@
 #error too old nghttp2 version, upgrade!
 #endif
 
+static int http2_perform_getsock(const struct connectdata *conn,
+                                 curl_socket_t *sock, /* points to
+                                                         numsocks
+                                                         number of
+                                                         sockets */
+                                 int numsocks)
+{
+  const struct http_conn *httpc = &conn->proto.httpc;
+  int bitmap = GETSOCK_BLANK;
+  (void)numsocks;
+
+  /* TODO We should check underlying socket state if it is SSL socket
+     because of renegotiation. */
+  sock[0] = conn->sock[FIRSTSOCKET];
+
+  if(nghttp2_session_want_read(httpc->h2))
+    bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+
+  if(nghttp2_session_want_write(httpc->h2))
+    bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+
+  return bitmap;
+}
+
+static int http2_getsock(struct connectdata *conn,
+                         curl_socket_t *sock, /* points to numsocks
+                                                 number of sockets */
+                         int numsocks)
+{
+  return http2_perform_getsock(conn, sock, numsocks);
+}
+
 /*
  * HTTP2 handler interface. This isn't added to the general list of protocols
  * but will be used at run-time when the protocol is dynamically switched from
@@ -56,10 +89,10 @@ const struct Curl_handler Curl_handler_http2 = {
   ZERO_NULL,                            /* connect_it */
   ZERO_NULL,                            /* connecting */
   ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
+  http2_getsock,                        /* proto_getsock */
+  http2_getsock,                        /* doing_getsock */
   ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
+  http2_perform_getsock,                /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* readwrite */
   PORT_HTTP,                            /* defport */
@@ -67,6 +100,25 @@ const struct Curl_handler Curl_handler_http2 = {
   PROTOPT_NONE                          /* flags */
 };
 
+const struct Curl_handler Curl_handler_http2_ssl = {
+  "HTTP2",                              /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  ZERO_NULL,                            /* do_it */
+  ZERO_NULL     ,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  http2_getsock,                        /* proto_getsock */
+  http2_getsock,                        /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  http2_perform_getsock,                /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  PORT_HTTP,                            /* defport */
+  CURLPROTO_HTTP | CURLPROTO_HTTPS,     /* protocol */
+  PROTOPT_SSL                           /* flags */
+};
 
 /*
  * Store nghttp2 version info in this buffer, Prefix with a space.  Return
@@ -326,6 +378,37 @@ static const nghttp2_session_callbacks callbacks = {
   on_header              /* nghttp2_on_header_callback */
 };
 
+static ssize_t data_source_read_callback(nghttp2_session *session,
+                                         int32_t stream_id,
+                                         uint8_t *buf, size_t length,
+                                         int *eof,
+                                         nghttp2_data_source *source,
+                                         void *userp)
+{
+  struct connectdata *conn = (struct connectdata *)userp;
+  struct http_conn *c = &conn->proto.httpc;
+  size_t nread;
+  (void)session;
+  (void)stream_id;
+  (void)eof;
+  (void)source;
+
+  nread = c->upload_len < length ? c->upload_len : length;
+  if(nread > 0) {
+    memcpy(buf, c->upload_mem, nread);
+    c->upload_mem += nread;
+    c->upload_len -= nread;
+    c->upload_left -= nread;
+  }
+
+  if(c->upload_left == 0)
+    *eof = 1;
+  else if(nread == 0)
+    return NGHTTP2_ERR_DEFERRED;
+
+  return nread;
+}
+
 /*
  * The HTTP2 settings we send in the Upgrade request
  */
@@ -428,6 +511,11 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
 
   (void)sockindex; /* we always do HTTP2 on sockindex 0 */
 
+  /* Nullify here because we call nghttp2_session_send() and they
+     might refer to the old buffer. */
+  httpc->upload_mem = NULL;
+  httpc->upload_len = 0;
+
   if(httpc->bodystarted &&
      httpc->nread_header_recvbuf < httpc->header_recvbuf->size_used) {
     size_t left =
@@ -527,10 +615,25 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
   size_t i;
   char *hdbuf = (char*)mem;
   char *end;
+  nghttp2_data_provider data_prd;
   (void)sockindex;
 
   infof(conn->data, "http2_send len=%zu\n", len);
 
+  if(httpc->stream_id != -1) {
+    /* If stream_id != -1, we have dispatched request HEADERS, and now
+       are going to send or sending request body in DATA frame */
+    httpc->upload_mem = mem;
+    httpc->upload_len = len;
+    nghttp2_session_resume_data(httpc->h2, httpc->stream_id);
+    rv = nghttp2_session_send(httpc->h2);
+    if(nghttp2_is_fatal(rv)) {
+      *err = CURLE_SEND_ERROR;
+      return -1;
+    }
+    return len - httpc->upload_len;
+  }
+
   /* Calculate number of headers contained in [mem, mem + len) */
   /* Here, we assume the curl http code generate *correct* HTTP header
      field block */
@@ -594,9 +697,31 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
     nva[i].valuelen = (uint16_t)(end - hdbuf);
 
     hdbuf = end + 2;
+    /* Inspect Content-Length header field and retrieve the request
+       entity length so that we can set END_STREAM to the last DATA
+       frame. */
+    if(nva[i].namelen == 14 &&
+       Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) {
+      size_t j;
+      for(j = 0; j < nva[i].valuelen; ++j) {
+        httpc->upload_left *= 10;
+        httpc->upload_left += nva[i].value[j] - '0';
+      }
+      infof(conn->data, "request content-length=%zu\n", httpc->upload_left);
+    }
   }
 
-  rv = nghttp2_submit_request(httpc->h2, 0, nva, nheader, NULL, NULL);
+  switch(conn->data->set.httpreq) {
+  case HTTPREQ_POST:
+  case HTTPREQ_POST_FORM:
+  case HTTPREQ_PUT:
+    data_prd.read_callback = data_source_read_callback;
+    data_prd.source.ptr = NULL;
+    rv = nghttp2_submit_request(httpc->h2, 0, nva, nheader, &data_prd, NULL);
+    break;
+  default:
+    rv = nghttp2_submit_request(httpc->h2, 0, nva, nheader, NULL, NULL);
+  }
 
   free(nva);
 
@@ -612,9 +737,17 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
     return -1;
   }
 
-  /* TODO: Still whole HEADERS frame may have not been sent because of
-     EAGAIN. But I don't know how to setup to call
-     nghttp2_session_send() when socket becomes writable. */
+  if(httpc->stream_id != -1) {
+    /* If whole HEADERS frame was sent off to the underlying socket,
+       the nghttp2 library calls data_source_read_callback. But only
+       it found that no data available, so it deferred the DATA
+       transmission. Which means that nghttp2_session_want_write()
+       returns 0 on http2_perform_getsock(), which results that no
+       writable socket check is performed. To workaround this, we
+       issue nghttp2_session_resume_data() here to bring back DATA
+       transmission from deferred state. */
+    nghttp2_session_resume_data(httpc->h2, httpc->stream_id);
+  }
 
   return len;
 }
@@ -627,7 +760,11 @@ int Curl_http2_switched(struct connectdata *conn)
   /* we are switched! */
   /* Don't know this is needed here at this moment. Original
      handler->flags is still useful. */
-  /* conn->handler = &Curl_handler_http2; */
+  if(conn->handler->flags & PROTOPT_SSL)
+    conn->handler = &Curl_handler_http2_ssl;
+  else
+    conn->handler = &Curl_handler_http2;
+
   httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
   httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
   conn->recv[FIRSTSOCKET] = http2_recv;
@@ -639,6 +776,9 @@ int Curl_http2_switched(struct connectdata *conn)
   httpc->nread_header_recvbuf = 0;
   httpc->data = NULL;
   httpc->datalen = 0;
+  httpc->upload_left = 0;
+  httpc->upload_mem = NULL;
+  httpc->upload_len = 0;
 
   conn->httpversion = 20;