You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by gm...@apache.org on 2022/02/16 18:34:24 UTC
[qpid-dispatch] branch main updated: DISPATCH-2326: Removed HTTP1/HTTP2/TCP adaptors and other adaptor related code
This is an automated email from the ASF dual-hosted git repository.
gmurthy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-dispatch.git
The following commit(s) were added to refs/heads/main by this push:
new d7ba0a8 DISPATCH-2326: Removed HTTP1/HTTP2/TCP adaptors and other adaptor related code
d7ba0a8 is described below
commit d7ba0a8557af57744a8e17b25f328e12d8db945a
Author: Ganesh Murthy <gm...@apache.org>
AuthorDate: Thu Jan 13 10:40:46 2022 -0500
DISPATCH-2326: Removed HTTP1/HTTP2/TCP adaptors and other adaptor related code
---
include/qpid/dispatch/router_core.h | 2 -
python/qpid_dispatch/management/qdrouter.json | 321 ---
python/qpid_dispatch_internal/dispatch.py | 8 -
python/qpid_dispatch_internal/management/agent.py | 58 -
python/qpid_dispatch_internal/management/config.py | 2 +-
src/CMakeLists.txt | 16 -
src/adaptors/adaptor_utils.c | 148 --
src/adaptors/adaptor_utils.h | 55 -
src/adaptors/http1/http1_adaptor.c | 705 -----
src/adaptors/http1/http1_client.c | 1827 -------------
src/adaptors/http1/http1_codec.c | 1769 ------------
src/adaptors/http1/http1_private.h | 271 --
src/adaptors/http1/http1_request_info.c | 46 -
src/adaptors/http1/http1_server.c | 1754 ------------
src/adaptors/http2/http2_adaptor.c | 2812 --------------------
src/adaptors/http2/http2_adaptor.h | 214 --
src/adaptors/http2/http2_adaptor_none.c | 72 -
src/adaptors/http_common.c | 714 -----
src/adaptors/http_common.h | 135 -
src/adaptors/reference_adaptor.c | 591 ----
src/adaptors/tcp_adaptor.c | 2072 ---------------
src/adaptors/tcp_adaptor.h | 105 -
src/amqp.c | 2 -
src/router_core/agent.c | 18 -
src/router_core/management_agent.c | 6 -
src/router_core/router_core.c | 5 +-
tests/CMakeLists.txt | 8 -
tests/c_benchmarks/CMakeLists.txt | 1 -
tests/c_benchmarks/bm_tcp_adapter.cpp | 363 ---
tests/friendship.proto | 63 -
tests/friendship_pb2.py | 377 ---
tests/friendship_pb2_grpc.py | 169 --
tests/friendship_server.py | 99 -
tests/http1_tests.py | 1557 -----------
tests/hyperh2_server.py | 116 -
tests/system_test.py | 48 -
tests/system_tests_grpc.py | 205 --
tests/system_tests_http1_adaptor.py | 969 -------
tests/system_tests_http1_over_tcp.py | 135 -
tests/system_tests_http2.py | 1052 --------
tests/system_tests_qdmanage.py | 2 +-
tests/system_tests_tcp_adaptor.py | 1262 ---------
42 files changed, 4 insertions(+), 20150 deletions(-)
diff --git a/include/qpid/dispatch/router_core.h b/include/qpid/dispatch/router_core.h
index 4bb5279..cc44c8b 100644
--- a/include/qpid/dispatch/router_core.h
+++ b/include/qpid/dispatch/router_core.h
@@ -184,8 +184,6 @@ typedef enum {
QD_ROUTER_CONFIG_LINK_ROUTE,
QD_ROUTER_CONFIG_AUTO_LINK,
QD_ROUTER_CONNECTION,
- QD_ROUTER_TCP_CONNECTION,
- QD_ROUTER_HTTP_REQUEST_INFO,
QD_ROUTER_ROUTER,
QD_ROUTER_LINK,
QD_ROUTER_ADDRESS,
diff --git a/python/qpid_dispatch/management/qdrouter.json b/python/qpid_dispatch/management/qdrouter.json
index d14f495..fbc5c63 100644
--- a/python/qpid_dispatch/management/qdrouter.json
+++ b/python/qpid_dispatch/management/qdrouter.json
@@ -535,11 +535,6 @@
"description":"Number of open connections to the router node.",
"graph": true
},
- "tcpConnectionCount": {
- "type": "integer",
- "description":"Number of open tcp connections at this router node.",
- "graph": true
- },
"presettledDeliveries": {
"type": "integer",
"description":"Number of presettled deliveries handled by the router.",
@@ -1082,276 +1077,6 @@
}
},
- "httpListener": {
- "description": "Ingress HTTP bridge.",
- "extends": "configurationEntity",
- "operations": ["CREATE", "DELETE"],
- "attributes": {
- "host": {
- "description":"A host name, IPV4 or IPV6 literal, or the empty string. The empty string listens on all local addresses. A host name listens on all addresses associated with the name. An IPV6 literal address (or wildcard '[::]') listens only for IPV6. An IPV4 literal address (or wildcard '0.0.0.0') listens only for IPV4.",
- "type": "string",
- "default": "0.0.0.0",
- "create": true
- },
- "address": {
- "description":"Address of this http bridge",
- "type": "string",
- "create": true
- },
- "port": {
- "description": "Port number or symbolic service name. If '0', the router shall assign an ephemeral port to the listener and log the port number with a log of the form 'SERVER (notice) Listening on <host>:<assigned-port> (<listener-name>)'",
- "type": "string",
- "create": true
- },
- "siteId": {
- "type": "string",
- "required": false,
- "description": "Used to identify where request is handled.",
- "create": true
- },
- "sslProfile": {
- "type": "string",
- "required": false,
- "description": "Name of the sslProfile..",
- "create": true
- },
- "protocolVersion": {
- "description": "The version of the HTTP protocol supported by this listener.",
- "type": [
- "HTTP1",
- "HTTP2"
- ],
- "default": "HTTP1",
- "required": false,
- "create": true
- },
- "aggregation": {
- "type": [
- "multipart",
- "json"
- ],
- "required": false,
- "description": "Aggregation mode for responses when used in conjunction with multicast address.",
- "create": true
- },
- "eventChannel": {
- "type": "boolean",
- "required": false,
- "description": "Enables restricted event mode where no responses are sent to request and only post is allowed",
- "create": true
- }
- }
- },
-
- "httpConnector": {
- "description": "Egress HTTP bridge.",
- "extends": "configurationEntity",
- "operations": ["CREATE", "DELETE"],
- "attributes": {
- "address": {
- "description":"Address of this bridge",
- "type": "string",
- "create": true
- },
- "host": {
- "description":"IP address: ipv4 or ipv6 literal or a host name",
- "type": "string",
- "default": "127.0.0.1",
- "create": true
- },
- "siteId": {
- "type": "string",
- "required": false,
- "description": "Used to identify where request originates.",
- "create": true
- },
- "port": {
- "description": "Port number or symbolic service name.",
- "type": "string",
- "create": true
-
- },
- "protocolVersion": {
- "description": "The version of the HTTP protocol supported by this connector.",
- "type": [
- "HTTP1",
- "HTTP2"
- ],
- "default": "HTTP1",
- "required": false,
- "create": true
- },
- "aggregation": {
- "type": [
- "multipart",
- "json"
- ],
- "required": false,
- "description": "Aggregation mode for responses when used in conjunction with multicast address.",
- "create": true
- },
- "eventChannel": {
- "type": "boolean",
- "required": false,
- "description": "Enables restricted event mode where no responses are sent to request and only post is allowed",
- "create": true
- },
- "hostOverride": {
- "type": "string",
- "required": false,
- "description": "Used to override the value of the Host header sent to the client.",
- "create": true
- }
- }
- },
-
- "httpRequestInfo": {
- "description": "Statistics for requests to/from httpListener and httpConnector instances",
- "extends": "operationalEntity",
- "attributes": {
- "address": {
- "description": "the router address over which the request was forwarded.",
- "type": "string"
- },
- "host": {
- "description": "IP address and port number of peer (i.e. the client for direction=in and the server for direction=out) in the form addr:port.",
- "type": "string"
- },
- "site": {
- "description": "The site identifier of the router in which the request originated (for direction=out) or was handled (for direction=in).",
- "type": "string"
- },
- "direction": {
- "description": "Direction of connection establishment in or out of the router.",
- "type": ["in", "out"]
- },
- "requests": {
- "type": "integer",
- "graph": true,
- "description": "The cumulative number of requests made."
- },
- "bytesIn": {
- "type": "integer",
- "graph": true,
- "description": "The cumulative number of bytes sent in requests."
- },
- "bytesOut": {
- "type": "integer",
- "graph": true,
- "description": "The cumulative number of bytes sent in responses."
- },
- "maxLatency": {
- "type": "integer",
- "graph": true,
- "description": "The maximum latency seen for any request represented by this instance."
- },
- "details": {
- "description": "Details of method and statuses for requests.",
- "type": "map"
- }
- }
- },
-
-
- "tcpListener": {
- "description": "Ingress TCP bridge.",
- "extends": "configurationEntity",
- "operations": ["CREATE", "DELETE"],
- "attributes": {
- "address": {
- "description":"Address of this bridge",
- "type": "string",
- "create": true
- },
- "host": {
- "description":"A host name, IPV4 or IPV6 literal, or the empty string. The empty string listens on all local addresses. A host name listens on all addresses associated with the name. An IPV6 literal address (or wildcard '[::]') listens only for IPV6. An IPV4 literal address (or wildcard '0.0.0.0') listens only for IPV4.",
- "type": "string",
- "default": "0.0.0.0",
- "create": true
- },
- "port": {
- "description": "Port number or symbolic service name. If '0', the router shall assign an ephemeral port to the listener and log the port number with a log of the form 'SERVER (notice) Listening on <host>:<assigned-port> (<listener-name>)'",
- "type": "string",
- "create": true
- },
- "siteId": {
- "type": "string",
- "required": false,
- "description": "Used to identify where connection is handled.",
- "create": true
- },
- "connectionsOpened": {
- "type": "integer",
- "graph": true,
- "description": "The number of connections opened by this listener."
- },
- "connectionsClosed": {
- "type": "integer",
- "graph": true,
- "description": "The number of connections closed by this listener."
- },
- "bytesIn": {
- "type": "integer",
- "graph": true,
- "description": "The number of bytes sent from clients to servers on all connections to this listener."
- },
- "bytesOut": {
- "type": "integer",
- "graph": true,
- "description": "The number of bytes sent from servers to clients on all connections to this listener."
- }
- }
- },
-
- "tcpConnector": {
- "description": "Egress TCP bridge.",
- "extends": "configurationEntity",
- "operations": ["CREATE", "DELETE"],
- "attributes": {
- "address": {
- "description":"Address of this bridge",
- "type": "string",
- "create": true
- },
- "host": {
- "description":"IP address: ipv4 or ipv6 literal or a host name",
- "type": "string",
- "create": true
- },
- "port": {
- "description": "Port number or symbolic service name.",
- "type": "string",
- "create": true
- },
- "siteId": {
- "type": "string",
- "required": false,
- "description": "Used to identify origin of connections.",
- "create": true
- },
- "connectionsOpened": {
- "type": "integer",
- "graph": true,
- "description": "The number of connections opened by this connector."
- },
- "connectionsClosed": {
- "type": "integer",
- "graph": true,
- "description": "The number of connections closed by this connector."
- },
- "bytesIn": {
- "type": "integer",
- "graph": true,
- "description": "The number of bytes sent from servers to clients on all connections created by this connector."
- },
- "bytesOut": {
- "type": "integer",
- "graph": true,
- "description": "The number of bytes sent from clients to servers on all connections created by this connector."
- }
- }
- },
-
"log": {
"description": "Configure logging for a particular module. You can use the `UPDATE` operation to change log settings while the router is running.",
"extends": "configurationEntity",
@@ -1375,8 +1100,6 @@
"CONN_MGR",
"PYTHON",
"PROTOCOL",
- "TCP_ADAPTOR",
- "HTTP_ADAPTOR",
"DEFAULT"
],
"required": true,
@@ -2100,50 +1823,6 @@
}
},
- "tcpConnection": {
- "description": "TCP Connections to/from the router's container.",
- "extends": "operationalEntity",
- "attributes": {
- "address": {
- "description": "the router address over which the tcp connection is forwarded.",
- "type": "string"
- },
- "host": {
- "description": "IP address and port number in the form addr:port.",
- "type": "string"
- },
- "direction": {
- "description": "Direction of connection establishment in or out of the router.",
- "type": ["in", "out"]
- },
- "bytesIn": {
- "type": "integer",
- "graph": true,
- "description": "The number of bytes sent from client to server on this connection."
- },
- "bytesOut": {
- "type": "integer",
- "graph": true,
- "description": "The number of bytes sent from server to client on this connection."
- },
- "uptimeSeconds": {
- "type": "integer",
- "graph": true,
- "description": "The number of seconds since the connection was created."
- },
- "lastInSeconds": {
- "type": "integer",
- "graph": true,
- "description": "The number of seconds since a delivery was sent from client to server on this connection."
- },
- "lastOutSeconds": {
- "type": "integer",
- "graph": true,
- "description": "The number of seconds since a delivery was sent from server to client on this connection."
- }
- }
- },
-
"allocator": {
"description": "Memory allocation pool.",
"extends": "operationalEntity",
diff --git a/python/qpid_dispatch_internal/dispatch.py b/python/qpid_dispatch_internal/dispatch.py
index aa8545a..5b288e3 100644
--- a/python/qpid_dispatch_internal/dispatch.py
+++ b/python/qpid_dispatch_internal/dispatch.py
@@ -64,14 +64,6 @@ class QdDll(ctypes.PyDLL):
self._prototype(self.qd_dispatch_configure_connector, ctypes.c_void_p, [self.qd_dispatch_p, py_object])
self._prototype(self.qd_dispatch_configure_ssl_profile, ctypes.c_void_p, [self.qd_dispatch_p, py_object])
self._prototype(self.qd_dispatch_configure_sasl_plugin, ctypes.c_void_p, [self.qd_dispatch_p, py_object])
- self._prototype(self.qd_dispatch_configure_tcp_listener, ctypes.c_void_p, [self.qd_dispatch_p, py_object])
- self._prototype(self.qd_dispatch_configure_tcp_connector, ctypes.c_void_p, [self.qd_dispatch_p, py_object])
- self._prototype(self.qd_dispatch_configure_http_listener, ctypes.c_void_p, [self.qd_dispatch_p, py_object])
- self._prototype(self.qd_dispatch_configure_http_connector, ctypes.c_void_p, [self.qd_dispatch_p, py_object])
- self._prototype(self.qd_dispatch_delete_tcp_listener, None, [self.qd_dispatch_p, ctypes.c_void_p])
- self._prototype(self.qd_dispatch_delete_tcp_connector, None, [self.qd_dispatch_p, ctypes.c_void_p])
- self._prototype(self.qd_dispatch_delete_http_listener, None, [self.qd_dispatch_p, ctypes.c_void_p])
- self._prototype(self.qd_dispatch_delete_http_connector, None, [self.qd_dispatch_p, ctypes.c_void_p])
self._prototype(self.qd_connection_manager_delete_listener, None, [self.qd_dispatch_p, ctypes.c_void_p])
self._prototype(self.qd_connection_manager_delete_connector, None, [self.qd_dispatch_p, ctypes.c_void_p])
self._prototype(self.qd_connection_manager_delete_ssl_profile, ctypes.c_bool, [self.qd_dispatch_p, ctypes.c_void_p])
diff --git a/python/qpid_dispatch_internal/management/agent.py b/python/qpid_dispatch_internal/management/agent.py
index 0e5b2a9..1aab2e9 100644
--- a/python/qpid_dispatch_internal/management/agent.py
+++ b/python/qpid_dispatch_internal/management/agent.py
@@ -584,64 +584,6 @@ class BindingEntity(EntityAdapter):
return super(BindingEntity, self).__str__().replace("Entity(", "BindingEntity(")
-class HttpListenerEntity(EntityAdapter):
- def create(self):
- return self._qd.qd_dispatch_configure_http_listener(self._dispatch, self)
-
- def _identifier(self):
- return _host_port_name_identifier(self)
-
- def __str__(self):
- return super(HttpListenerEntity, self).__str__().replace("Entity(", "HttpListenerEntity(")
-
- def _delete(self):
- self._qd.qd_dispatch_delete_http_listener(self._dispatch, self._implementations[0].key)
-
-
-class TcpListenerEntity(EntityAdapter):
- def create(self):
- config_listener = self._qd.qd_dispatch_configure_tcp_listener(self._dispatch, self)
- return config_listener
-
- def _identifier(self):
- return _host_port_name_identifier(self)
-
- def __str__(self):
- return super(TcpListenerEntity, self).__str__().replace("Entity(", "TcpListenerEntity(")
-
- def _delete(self):
- self._qd.qd_dispatch_delete_tcp_listener(self._dispatch, self._implementations[0].key)
-
-
-class TcpConnectorEntity(EntityAdapter):
- def create(self):
- config_connector = self._qd.qd_dispatch_configure_tcp_connector(self._dispatch, self)
- return config_connector
-
- def _identifier(self):
- return _host_port_name_identifier(self)
-
- def __str__(self):
- return super(TcpConnectorEntity, self).__str__().replace("Entity(", "TcpConnectorEntity(")
-
- def _delete(self):
- self._qd.qd_dispatch_delete_tcp_connector(self._dispatch, self._implementations[0].key)
-
-
-class HttpConnectorEntity(EntityAdapter):
- def create(self):
- return self._qd.qd_dispatch_configure_http_connector(self._dispatch, self)
-
- def _identifier(self):
- return _host_port_name_identifier(self)
-
- def __str__(self):
- return super(HttpConnectorEntity, self).__str__().replace("Entity(", "HttpConnectorEntity(")
-
- def _delete(self):
- self._qd.qd_dispatch_delete_http_connector(self._dispatch, self._implementations[0].key)
-
-
class EntityCache:
"""
Searchable cache of entities, can be refreshed from implementation objects.
diff --git a/python/qpid_dispatch_internal/management/config.py b/python/qpid_dispatch_internal/management/config.py
index f949d35..50f64bf 100644
--- a/python/qpid_dispatch_internal/management/config.py
+++ b/python/qpid_dispatch_internal/management/config.py
@@ -319,7 +319,7 @@ def configure_dispatch(dispatch, lib_handle, filename):
for t in "sslProfile", "authServicePlugin", \
"router.config.address", "router.config.linkRoute", "router.config.autoLink", \
"router.config.exchange", "router.config.binding", \
- "vhost", "httpListener", "httpConnector", "tcpListener", "tcpConnector":
+ "vhost":
for a in config.by_type(t):
configure(a)
if t == "sslProfile":
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3ea0183..e22b81a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -38,15 +38,6 @@ add_custom_command (
# Build the qpid-dispatch library.
set(qpid_dispatch_SOURCES
- adaptors/reference_adaptor.c
- adaptors/adaptor_utils.c
- adaptors/http_common.c
- adaptors/http1/http1_codec.c
- adaptors/http1/http1_adaptor.c
- adaptors/http1/http1_client.c
- adaptors/http1/http1_server.c
- adaptors/http1/http1_request_info.c
- adaptors/tcp_adaptor.c
alloc.c
alloc_pool.c
aprintf.c
@@ -161,13 +152,6 @@ else(USE_LIBWEBSOCKETS)
list(APPEND qpid_dispatch_SOURCES http-none.c)
endif(USE_LIBWEBSOCKETS)
-if(USE_LIBNGHTTP2)
- list(APPEND qpid_dispatch_SOURCES adaptors/http2/http2_adaptor.c)
- list(APPEND qpid_dispatch_INCLUDES ${NGHTTP2_INCLUDE_DIRS})
- list(APPEND qpid_dispatch_LIBRARIES ${NGHTTP2_LIBRARIES})
-else(USE_LIBNGHTTP2)
- list(APPEND qpid_dispatch_SOURCES adaptors/http2/http2_adaptor_none.c)
-endif(USE_LIBNGHTTP2)
# DISPATCH-654 There are, in fact, no strict-aliasing violations and newer compilers don't complain.
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
diff --git a/src/adaptors/adaptor_utils.c b/src/adaptors/adaptor_utils.c
deleted file mode 100644
index 71f4199..0000000
--- a/src/adaptors/adaptor_utils.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "adaptor_utils.h"
-
-#include <proton/netaddr.h>
-
-#include <string.h>
-
-
-#define RAW_BUFFER_BATCH 16
-
-
-char *qda_raw_conn_get_address(pn_raw_connection_t *socket)
-{
- const pn_netaddr_t *netaddr = pn_raw_connection_remote_addr(socket);
- char buffer[1024];
- int len = pn_netaddr_str(netaddr, buffer, 1024);
- if (len <= 1024) {
- return strdup(buffer);
- } else {
- return strndup(buffer, 1024);
- }
-}
-
-
-void qda_raw_conn_get_read_buffers(pn_raw_connection_t *conn, qd_buffer_list_t *blist, uintmax_t *length)
-{
- pn_raw_buffer_t buffs[RAW_BUFFER_BATCH];
-
- DEQ_INIT(*blist);
- uintmax_t len = 0;
- size_t n;
- while ((n = pn_raw_connection_take_read_buffers(conn, buffs, RAW_BUFFER_BATCH)) != 0) {
- for (size_t i = 0; i < n; ++i) {
- qd_buffer_t *qd_buf = (qd_buffer_t*)buffs[i].context;
- assert(qd_buf);
- if (buffs[i].size) {
- // set content length:
- qd_buffer_insert(qd_buf, buffs[i].size);
- len += buffs[i].size;
- DEQ_INSERT_TAIL(*blist, qd_buf);
- } else { // ignore empty buffers
- qd_buffer_free(qd_buf);
- }
- }
- }
-
- *length = len;
-}
-
-
-int qda_raw_conn_grant_read_buffers(pn_raw_connection_t *conn)
-{
- pn_raw_buffer_t buffs[RAW_BUFFER_BATCH];
- if (pn_raw_connection_is_read_closed(conn))
- return 0;
-
- int granted = 0;
- size_t count = pn_raw_connection_read_buffers_capacity(conn);
- while (count) {
- size_t batch_ct = 0;
- for (int i = 0; i < RAW_BUFFER_BATCH; ++i) {
- qd_buffer_t *buf = qd_buffer();
- buffs[i].context = (intptr_t)buf;
- buffs[i].bytes = (char*) qd_buffer_base(buf);
- buffs[i].capacity = qd_buffer_capacity(buf);
- buffs[i].size = 0;
- buffs[i].offset = 0;
- batch_ct += 1;
- count -= 1;
- if (count == 0)
- break;
- }
- pn_raw_connection_give_read_buffers(conn, buffs, batch_ct);
- granted += batch_ct;
- }
-
- return granted;
-}
-
-
-int qda_raw_conn_write_buffers(pn_raw_connection_t *conn, qd_buffer_list_t *blist, size_t offset)
-{
- pn_raw_buffer_t buffs[RAW_BUFFER_BATCH];
- size_t count = pn_raw_connection_write_buffers_capacity(conn);
- count = MIN(count, DEQ_SIZE(*blist));
- int sent = 0;
-
- // Since count is set to ensure that we never run out of capacity or
- // buffers to send we can avoid checking that on every loop
-
- while (count) {
- size_t batch_ct = 0;
-
- for (int i = 0; i < RAW_BUFFER_BATCH; ++i) {
- qd_buffer_t *buf = DEQ_HEAD(*blist);
- DEQ_REMOVE_HEAD(*blist);
-
- buffs[i].context = (intptr_t)buf;
- buffs[i].bytes = (char*)qd_buffer_base(buf);
- buffs[i].capacity = 0;
- buffs[i].size = qd_buffer_size(buf) - offset;
- buffs[i].offset = offset;
- offset = 0; // all succeeding bufs start at base
-
- batch_ct += 1;
- count -= 1;
- if (count == 0)
- break;
- }
- pn_raw_connection_write_buffers(conn, buffs, batch_ct);
- sent += batch_ct;
- }
-
- return sent;
-}
-
-
-void qda_raw_conn_free_write_buffers(pn_raw_connection_t *conn)
-{
- pn_raw_buffer_t buffs[RAW_BUFFER_BATCH];
- size_t n;
- while ((n = pn_raw_connection_take_written_buffers(conn, buffs, RAW_BUFFER_BATCH)) != 0) {
- for (size_t i = 0; i < n; ++i) {
- qd_buffer_t *qd_buf = (qd_buffer_t*)buffs[i].context;
- assert(qd_buf);
- qd_buffer_free(qd_buf);
- }
- }
-}
-
diff --git a/src/adaptors/adaptor_utils.h b/src/adaptors/adaptor_utils.h
deleted file mode 100644
index 58f7cd6..0000000
--- a/src/adaptors/adaptor_utils.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef __adaptor_utils_h__
-#define __adaptor_utils_h__
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "qpid/dispatch/buffer.h"
-
-#include <proton/raw_connection.h>
-
-// Get the raw connections remote address
-// Caller must free() the result when done.
-//
-char *qda_raw_conn_get_address(pn_raw_connection_t *socket);
-
-// Retrieve all available incoming data buffers from the raw connection.
-// Return the result in blist, with the total number of read octets in *length
-// Note: only those buffers containing data (size != 0) are returned.
-//
-void qda_raw_conn_get_read_buffers(pn_raw_connection_t *conn, qd_buffer_list_t *blist, uintmax_t *length);
-
-// allocate empty read buffers to the raw connection. This will provide enough buffers
-// to fill the connections capacity. Returns the number of buffers granted.
-//
-int qda_raw_conn_grant_read_buffers(pn_raw_connection_t *conn);
-
-
-// Write blist buffers to the connection. Buffers are removed from the HEAD of
-// blist. Returns the actual number of buffers taken. offset is an optional
-// offset to the first outgoing octet in the HEAD buffer.
-//
-int qda_raw_conn_write_buffers(pn_raw_connection_t *conn, qd_buffer_list_t *blist, size_t offset);
-
-
-// release all sent buffers held by the connection
-//
-void qda_raw_conn_free_write_buffers(pn_raw_connection_t *conn);
-
-
-#endif // __adaptor_utils_h__
diff --git a/src/adaptors/http1/http1_adaptor.c b/src/adaptors/http1/http1_adaptor.c
deleted file mode 100644
index 86f782a..0000000
--- a/src/adaptors/http1/http1_adaptor.c
+++ /dev/null
@@ -1,705 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "http1_private.h"
-
-#include <inttypes.h>
-#include <stdio.h>
-
-//
-// This file contains code for the HTTP/1.x protocol adaptor. This file
-// includes code that is common to both ends of the protocol (e.g. client and
-// server processing). See http1_client.c and http1_server.c for code specific
-// to HTTP endpoint processing.
-//
-
-#define RAW_BUFFER_BATCH 16
-
-
-/*
- HTTP/1.x <--> AMQP message mapping
-
- Message Properties Section:
-
- HTTP Message AMQP Message Properties
- ------------ -----------------------
- Request Method subject field (request message)
- Response Status code subject field (response message)
-
- Application Properties Section:
-
- HTTP Message AMQP Message App Properties Map
- ------------ -------------------------------
- Message Version ":version": <version|"1.1" by default>
- Response Reason ":reason": <string> (response only)
- Request Target ":path": <string>
- * "<lowercase(key)>" <string>
-
- Notes:
- - Message App Properties Keys that start with ":" are reserved by the
- adaptor for meta-data.
- */
-
-
-ALLOC_DEFINE(qdr_http1_out_data_t);
-ALLOC_DEFINE(qdr_http1_connection_t);
-
-
-qdr_http1_adaptor_t *qdr_http1_adaptor;
-
-
-void qdr_http1_request_base_cleanup(qdr_http1_request_base_t *hreq)
-{
- if (hreq) {
- DEQ_REMOVE(hreq->hconn->requests, hreq);
- h1_codec_request_state_cancel(hreq->lib_rs);
- free(hreq->response_addr);
- free(hreq->site);
- }
-}
-
-
-void qdr_http1_connection_free(qdr_http1_connection_t *hconn)
-{
- if (hconn) {
- pn_raw_connection_t *rconn = 0;
- qd_timer_t *timer = 0;
-
- // prevent core from activating this connection while it is being torn
- // down. Also prevent timer callbacks from running. see
- // _core_connection_activate_CT and _do_reconnect in http1_server.c
- //
- sys_mutex_lock(qdr_http1_adaptor->lock);
- {
- DEQ_REMOVE(qdr_http1_adaptor->connections, hconn);
- timer = hconn->server.reconnect_timer;
- hconn->server.reconnect_timer = 0;
- rconn = hconn->raw_conn;
- hconn->raw_conn = 0;
- if (hconn->server.connector) {
- hconn->server.connector->ctx = 0;
- hconn->server.connector = 0;
- }
- if (hconn->qdr_conn)
- qdr_connection_set_context(hconn->qdr_conn, 0);
- hconn->qdr_conn = 0;
- }
- sys_mutex_unlock(qdr_http1_adaptor->lock);
-
- // must free timer outside of lock since callback
- // attempts to take lock:
- qd_timer_free(timer);
-
- // cleanup outstanding requests
- //
- if (hconn->type == HTTP1_CONN_SERVER)
- qdr_http1_server_conn_cleanup(hconn);
- else
- qdr_http1_client_conn_cleanup(hconn);
-
- h1_codec_connection_free(hconn->http_conn);
- if (rconn) {
- pn_raw_connection_set_context(rconn, 0);
- pn_raw_connection_close(rconn);
- }
-
- sys_atomic_destroy(&hconn->q2_restart);
-
- free(hconn->cfg.host);
- free(hconn->cfg.port);
- free(hconn->cfg.address);
- free(hconn->cfg.site);
- free(hconn->cfg.host_override);
- free(hconn->cfg.host_port);
-
- free(hconn->client.client_ip_addr);
- free(hconn->client.reply_to_addr);
-
- free_qdr_http1_connection_t(hconn);
- }
-}
-
-static void _free_qdr_http1_out_data(qdr_http1_out_data_t *od)
-{
- if (od) {
- qd_iterator_free(od->data_iter);
- if (od->stream_data)
- qd_message_stream_data_release(od->stream_data);
- else
- qd_buffer_list_free_buffers(&od->raw_buffers);
- free_qdr_http1_out_data_t(od);
- }
-}
-
-void qdr_http1_out_data_cleanup(qdr_http1_out_data_list_t *out_data)
-{
- if (out_data) {
- // expect: all buffers returned from proton!
- // FIXME: not during router shutdown!
- // assert(qdr_http1_out_data_buffers_outstanding(out_data) == 0);
- qdr_http1_out_data_t *od = DEQ_HEAD(*out_data);
- while (od) {
- DEQ_REMOVE_HEAD(*out_data);
- _free_qdr_http1_out_data(od);
- od = DEQ_HEAD(*out_data);
- }
- }
-}
-
-
-// Initiate close of the raw connection.
-//
-void qdr_http1_close_connection(qdr_http1_connection_t *hconn, const char *error)
-{
- if (error) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_ERROR,
- "[C%"PRIu64"] Connection closing: %s", hconn->conn_id, error);
- }
-
- if (hconn->raw_conn) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"] Initiating close of connection", hconn->conn_id);
- pn_raw_connection_close(hconn->raw_conn);
- }
-
- // clean up all connection related stuff on PN_RAW_CONNECTION_DISCONNECTED
- // event
-}
-
-
-void qdr_http1_rejected_response(qdr_http1_request_base_t *hreq,
- const qdr_error_t *error)
-{
- char *reason = 0;
- if (error) {
- size_t len = 0;
- char *ename = qdr_error_name(error);
- char *edesc = qdr_error_description(error);
- if (ename) len += strlen(ename);
- if (edesc) len += strlen(edesc);
- if (len) {
- reason = qd_malloc(len + 2);
- reason[0] = 0;
- if (ename) {
- strcat(reason, ename);
- strcat(reason, " ");
- }
- if (edesc)
- strcat(reason, edesc);
- }
- free(ename);
- free(edesc);
- }
-
- qdr_http1_error_response(hreq, HTTP1_STATUS_BAD_REQ,
- reason ? reason : "Invalid Request");
- free(reason);
-}
-
-
-// send a server error response
-//
-void qdr_http1_error_response(qdr_http1_request_base_t *hreq,
- int error_code,
- const char *reason)
-{
- if (hreq->lib_rs) {
- bool ignored;
- h1_codec_tx_response(hreq->lib_rs, error_code, reason, 1, 1);
- h1_codec_tx_add_header(hreq->lib_rs, "Content-Length", "0");
- h1_codec_tx_done(hreq->lib_rs, &ignored);
- }
-}
-
-
-//
-// Raw Connection Write Buffer Management
-//
-
-
-// Write list of data out the raw connection, freeing entries when data is exhausted
-//
-uint64_t qdr_http1_write_out_data(qdr_http1_connection_t *hconn, qdr_http1_out_data_list_t *fifo)
-{
- size_t count = !hconn->raw_conn || pn_raw_connection_is_write_closed(hconn->raw_conn)
- ? 0
- : pn_raw_connection_write_buffers_capacity(hconn->raw_conn);
-
- if (hconn->write_buf_busy || count == 0)
- return 0;
-
- const size_t max_octets = HTTP1_IO_BUF_SIZE;
- size_t total_octets = 0;
- while (!DEQ_IS_EMPTY(*fifo) && total_octets < max_octets) {
- qdr_http1_out_data_t *od = DEQ_HEAD(*fifo);
- uint32_t data_octets = qd_iterator_remaining(od->data_iter);
-
- size_t len = MIN(data_octets, max_octets - total_octets);
- int copied = qd_iterator_ncopy(od->data_iter, &hconn->write_buffer[total_octets], len);
- assert(copied == len);
- data_octets -= copied;
- total_octets += copied;
-
- qd_iterator_trim_view(od->data_iter, data_octets);
- if (qd_iterator_remaining(od->data_iter) == 0) {
- DEQ_REMOVE_HEAD(*fifo);
- _free_qdr_http1_out_data(od);
- }
- }
-
- if (total_octets) {
- pn_raw_buffer_t pn_buff = {0};
- pn_buff.bytes = (char*) hconn->write_buffer;
- pn_buff.size = total_octets;
-
- // keep me, you'll need it
- if (HTTP1_DUMP_BUFFERS) {
- fprintf(stdout, "\n[C%"PRIu64"] Raw Write: Ptr=%p len=%"PRIu32"\n value='%.*s'\n",
- hconn->conn_id, (void*)pn_buff.bytes, pn_buff.size,
- (int) pn_buff.size, pn_buff.bytes);
- fflush(stdout);
- }
-
- pn_raw_connection_write_buffers(hconn->raw_conn, &pn_buff, 1);
- hconn->write_buf_busy = true;
- }
-
- hconn->out_http1_octets += total_octets;
- return total_octets;
-}
-
-
-// The HTTP encoder has a list of buffers to be written to the raw connection.
-// Queue it to the outgoing data fifo.
-//
-void qdr_http1_enqueue_buffer_list(qdr_http1_out_data_list_t *fifo, qd_buffer_list_t *blist, uintmax_t octets)
-{
- if (octets) {
- qdr_http1_out_data_t *od = new_qdr_http1_out_data_t();
- ZERO(od);
- od->raw_buffers = *blist;
- od->data_iter = qd_iterator_buffer(DEQ_HEAD(od->raw_buffers), 0, (int)octets, ITER_VIEW_ALL);
- DEQ_INIT(*blist);
- DEQ_INSERT_TAIL(*fifo, od);
- }
-}
-
-
-// The HTTP encoder has a message body data to be written to the raw connection.
-// Queue it to the outgoing data fifo.
-//
-void qdr_http1_enqueue_stream_data(qdr_http1_out_data_list_t *fifo, qd_message_stream_data_t *stream_data)
-{
- if (qd_message_stream_data_payload_length(stream_data)) {
- qdr_http1_out_data_t *od = new_qdr_http1_out_data_t();
- ZERO(od);
- od->stream_data = stream_data;
- od->data_iter = qd_message_stream_data_iterator(stream_data);
- DEQ_INSERT_TAIL(*fifo, od);
- } else {
- // empty body-data
- qd_message_stream_data_release(stream_data);
- }
-}
-
-
-// Called during proactor event PN_RAW_CONNECTION_WRITTEN
-//
-void qdr_http1_free_written_buffers(qdr_http1_connection_t *hconn)
-{
- pn_raw_buffer_t pn_buff = {0};
-
- if (pn_raw_connection_take_written_buffers(hconn->raw_conn, &pn_buff, 1) != 0) {
- assert(hconn->write_buf_busy); // expect write buffer in use
-
- // keep me, you'll need it
- if (HTTP1_DUMP_BUFFERS) {
- char *ptr = (char*) pn_buff.bytes;
- int len = (int) pn_buff.size;
- fprintf(stdout, "\n[C%"PRIu64"] Raw Written: Ptr=%p len=%d c=%d o=%d\n value='%.*s'\n",
- hconn->conn_id, (void*)ptr, len, pn_buff.capacity, pn_buff.offset, len, ptr);
- fflush(stdout);
- }
-
- hconn->write_buf_busy = false;
- }
-}
-
-
-//
-// Raw Connection Read Buffer Management
-//
-
-int qdr_http1_grant_read_buffers(qdr_http1_connection_t *hconn)
-{
- if (!hconn->read_buf_busy && hconn->raw_conn
- && pn_raw_connection_read_buffers_capacity(hconn->raw_conn) > 0) {
-
- pn_raw_buffer_t pn_buf = {0};
- pn_buf.bytes = (char*) hconn->read_buffer;
- pn_buf.capacity = HTTP1_IO_BUF_SIZE;
- pn_raw_connection_give_read_buffers(hconn->raw_conn, &pn_buf, 1);
- hconn->read_buf_busy = true;
- return 1;
- }
- return 0;
-}
-
-// take incoming data from raw connection
-uintmax_t qdr_http1_get_read_buffers(qdr_http1_connection_t *hconn,
- qd_buffer_list_t *blist)
-{
- pn_raw_buffer_t pn_buff;
- DEQ_INIT(*blist);
- uintmax_t octets = 0;
-
- if (hconn->raw_conn && pn_raw_connection_take_read_buffers(hconn->raw_conn,
- &pn_buff, 1)) {
- if (pn_buff.size) {
- octets = pn_buff.size;
- qd_buffer_list_append(blist, (uint8_t*) pn_buff.bytes, pn_buff.size);
- }
- hconn->read_buf_busy = false;
- }
- return octets;
-}
-
-
-// Per-message callback to resume receiving after Q2 is unblocked on the
-// incoming link (to HTTP app). This routine runs on another I/O thread so it
-// must be thread safe!
-//
-void qdr_http1_q2_unblocked_handler(const qd_alloc_safe_ptr_t context)
-{
- // prevent the hconn from being deleted while running:
- sys_mutex_lock(qdr_http1_adaptor->lock);
-
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*)qd_alloc_deref_safe_ptr(&context);
- if (hconn && hconn->raw_conn) {
- sys_atomic_set(&hconn->q2_restart, 1);
- pn_raw_connection_wake(hconn->raw_conn);
- }
-
- sys_mutex_unlock(qdr_http1_adaptor->lock);
-}
-
-
-//
-// Protocol Adaptor Callbacks
-//
-
-
-// Invoked by the core/mgmt thread to wake an I/O thread for the connection.
-// Must be thread safe.
-//
-static void _core_connection_activate_CT(void *context, qdr_connection_t *conn)
-{
- bool activated = false;
-
- sys_mutex_lock(qdr_http1_adaptor->lock);
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_connection_get_context(conn);
- if (hconn) {
- if (hconn->raw_conn) {
- pn_raw_connection_wake(hconn->raw_conn);
- activated = true;
- } else if (hconn->server.reconnect_timer) {
- assert(hconn->type == HTTP1_CONN_SERVER);
- qd_timer_schedule(hconn->server.reconnect_timer, 0);
- activated = true;
- }
- }
- sys_mutex_unlock(qdr_http1_adaptor->lock);
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"] Connection %s",
- conn->identity, activated ? "activated" : "down, unable to activate");
-}
-
-
-static void _core_link_first_attach(void *context,
- qdr_connection_t *conn,
- qdr_link_t *link,
- qdr_terminus_t *source,
- qdr_terminus_t *target,
- qd_session_class_t ssn_class)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_connection_get_context(conn);
- if (hconn)
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"] Link first attach", hconn->conn_id);
-}
-
-
-static void _core_link_second_attach(void *context,
- qdr_link_t *link,
- qdr_terminus_t *source,
- qdr_terminus_t *target)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_link_get_context(link);
- if (!hconn) return;
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Link second attach", hconn->conn_id, link->identity);
-
- if (hconn->type == HTTP1_CONN_CLIENT) {
- qdr_http1_client_core_second_attach((qdr_http1_adaptor_t*) context,
- hconn, link, source, target);
- }
-}
-
-
-static void _core_link_detach(void *context, qdr_link_t *link, qdr_error_t *error, bool first, bool close)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_link_get_context(link);
- if (hconn) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Link detach", hconn->conn_id, link->identity);
-
- qdr_link_set_context(link, 0);
- if (link == hconn->out_link)
- hconn->out_link = 0;
- else
- hconn->in_link = 0;
- }
-}
-
-
-static void _core_link_flow(void *context, qdr_link_t *link, int credit)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_link_get_context(link);
- if (hconn) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Link flow (%d)",
- hconn->conn_id, link->identity, credit);
- if (hconn->type == HTTP1_CONN_SERVER)
- qdr_http1_server_core_link_flow((qdr_http1_adaptor_t*) context, hconn, link, credit);
- else
- qdr_http1_client_core_link_flow((qdr_http1_adaptor_t*) context, hconn, link, credit);
- }
-}
-
-
-static void _core_link_offer(void *context, qdr_link_t *link, int delivery_count)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_link_get_context(link);
- if (hconn) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Link offer (%d)",
- hconn->conn_id, link->identity, delivery_count);
- }
-}
-
-
-static void _core_link_drained(void *context, qdr_link_t *link)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_link_get_context(link);
- if (hconn) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Link drained",
- hconn->conn_id, link->identity);
- }
-}
-
-
-static void _core_link_drain(void *context, qdr_link_t *link, bool mode)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_link_get_context(link);
- if (hconn) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Link drain %s",
- hconn->conn_id, link->identity,
- mode ? "ON" : "OFF");
- }
-}
-
-
-static int _core_link_push(void *context, qdr_link_t *link, int limit)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_link_get_context(link);
- if (hconn) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Link push %d", hconn->conn_id, link->identity, limit);
- return qdr_link_process_deliveries(qdr_http1_adaptor->core, link, limit);
- }
- return 0;
-}
-
-
-// The I/O thread wants to send this delivery out the link
-//
-static uint64_t _core_link_deliver(void *context, qdr_link_t *link, qdr_delivery_t *delivery, bool settled)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_link_get_context(link);
- uint64_t outcome = PN_RELEASED;
-
- if (hconn) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- DLV_FMT" Core link deliver (%s)", DLV_ARGS(delivery),
- settled ? "settled" : "unsettled");
-
- if (hconn->type == HTTP1_CONN_SERVER)
- outcome = qdr_http1_server_core_link_deliver(qdr_http1_adaptor, hconn, link, delivery, settled);
- else
- outcome = qdr_http1_client_core_link_deliver(qdr_http1_adaptor, hconn, link, delivery, settled);
- }
-
- return outcome;
-}
-
-static int _core_link_get_credit(void *context, qdr_link_t *link)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_link_get_context(link);
- int credit = 0;
- if (hconn) {
- credit = (link == hconn->in_link) ? hconn->in_link_credit : hconn->out_link_credit;
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Link get credit (%d)", hconn->conn_id, link->identity, credit);
- }
-
- return credit;
-}
-
-
-// Handle disposition/settlement update for the outstanding incoming HTTP message.
-//
-static void _core_delivery_update(void *context, qdr_delivery_t *dlv, uint64_t disp, bool settled)
-{
- qdr_http1_request_base_t *hreq = (qdr_http1_request_base_t*) qdr_delivery_get_context(dlv);
- if (hreq) {
- qdr_http1_connection_t *hconn = hreq->hconn;
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- DLV_FMT" core delivery update disp=0x%"PRIx64" %s",
- DLV_ARGS(dlv), disp,
- settled ? "settled" : "unsettled");
-
- if (hconn->type == HTTP1_CONN_SERVER)
- qdr_http1_server_core_delivery_update(qdr_http1_adaptor, hconn, hreq, dlv, disp, settled);
- else
- qdr_http1_client_core_delivery_update(qdr_http1_adaptor, hconn, hreq, dlv, disp, settled);
- }
-}
-
-
-// This is invoked by management to initiate the connection close process.
-// Once the raw conn is closed (DISCONNECT event) we call qdr_connection_closed()
-// to finish the close processing
-//
-static void _core_conn_close(void *context, qdr_connection_t *conn, qdr_error_t *error)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_connection_get_context(conn);
- if (hconn) {
- assert(hconn->qdr_conn == conn);
- char *desc = error ? qdr_error_description(error) : 0;
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_INFO,
- "[C%"PRIu64"] HTTP/1.x %s", hconn->conn_id,
- desc ? desc : "connection closed by management");
-
- if (hconn->type == HTTP1_CONN_SERVER)
- qdr_http1_server_core_conn_close(qdr_http1_adaptor, hconn);
- else
- qdr_http1_client_core_conn_close(qdr_http1_adaptor, hconn);
- free(desc);
- }
-}
-
-
-static void _core_conn_trace(void *context, qdr_connection_t *conn, bool trace)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) qdr_connection_get_context(conn);
- if (hconn) {
- hconn->trace = trace;
- if (trace)
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"] HTTP/1.x trace enabled", hconn->conn_id);
- }
-}
-
-
-//
-// Adaptor Setup & Teardown
-//
-
-
-static void qd_http1_adaptor_init(qdr_core_t *core, void **adaptor_context)
-{
- qdr_http1_adaptor_t *adaptor = NEW(qdr_http1_adaptor_t);
-
- ZERO(adaptor);
- adaptor->core = core;
- adaptor->log = qd_log_source(QD_HTTP_LOG_SOURCE);
- adaptor->lock = sys_mutex();
- DEQ_INIT(adaptor->listeners);
- DEQ_INIT(adaptor->connectors);
- DEQ_INIT(adaptor->connections);
- adaptor->adaptor = qdr_protocol_adaptor(core,
- "http/1.x",
- adaptor, // context
- _core_connection_activate_CT, // core thread only
- _core_link_first_attach,
- _core_link_second_attach,
- _core_link_detach,
- _core_link_flow,
- _core_link_offer,
- _core_link_drained,
- _core_link_drain,
- _core_link_push,
- _core_link_deliver,
- _core_link_get_credit,
- _core_delivery_update,
- _core_conn_close,
- _core_conn_trace);
- *adaptor_context = adaptor;
- qdr_http1_adaptor = adaptor;
-}
-
-
-static void qd_http1_adaptor_final(void *adaptor_context)
-{
- qdr_http1_adaptor_t *adaptor = (qdr_http1_adaptor_t*) adaptor_context;
- qdr_protocol_adaptor_free(adaptor->core, adaptor->adaptor);
-
- qdr_http1_connection_t *hconn = DEQ_HEAD(adaptor->connections);
- while (hconn) {
- qdr_http1_connection_free(hconn);
- hconn = DEQ_HEAD(adaptor->connections);
- }
- qd_http_listener_t *li = DEQ_HEAD(adaptor->listeners);
- while (li) {
- DEQ_REMOVE_HEAD(qdr_http1_adaptor->listeners);
- qd_http_listener_decref(li);
- li = DEQ_HEAD(adaptor->listeners);
- }
- qd_http_connector_t *ct = DEQ_HEAD(adaptor->connectors);
- while (ct) {
- DEQ_REMOVE_HEAD(qdr_http1_adaptor->connectors);
- qd_http_connector_decref(ct);
- ct = DEQ_HEAD(adaptor->connectors);
- }
-
- sys_mutex_free(adaptor->lock);
- qdr_http1_adaptor = NULL;
-
- free(adaptor);
-}
-
-
-/**
- * Declare the adaptor so that it will self-register on process startup.
- */
-QDR_CORE_ADAPTOR_DECLARE("http1.x-adaptor", qd_http1_adaptor_init, qd_http1_adaptor_final)
-
diff --git a/src/adaptors/http1/http1_client.c b/src/adaptors/http1/http1_client.c
deleted file mode 100644
index 33d75c0..0000000
--- a/src/adaptors/http1/http1_client.c
+++ /dev/null
@@ -1,1827 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "python_private.h"
-
-#include "adaptors/adaptor_utils.h"
-#include "http1_private.h"
-
-#include <proton/listener.h>
-#include <proton/proactor.h>
-#include <proton/netaddr.h>
-
-
-//
-// This file contains code specific to HTTP client processing. The raw
-// connection is terminated at an HTTP client, not an HTTP server.
-//
-
-#define DEFAULT_CAPACITY 250
-#define LISTENER_BACKLOG 16
-
-const char *CONTENT_LENGTH_KEY = "Content-Length";
-const char *POST_METHOD = "POST";
-
-//
-// State for a single response message to be sent to the client via the raw
-// connection.
-//
-typedef struct _client_response_msg_t {
- DEQ_LINKS(struct _client_response_msg_t);
-
- qdr_delivery_t *dlv; // from core via core_link_deliver
- uint64_t dispo; // set by adaptor on encode complete
- bool headers_encoded; // all headers completely encoded
- bool encoded; // true when full response encoded
-
- // HTTP encoded message data
- qdr_http1_out_data_list_t out_data;
-
-} _client_response_msg_t;
-ALLOC_DECLARE(_client_response_msg_t);
-ALLOC_DEFINE(_client_response_msg_t);
-DEQ_DECLARE(_client_response_msg_t, _client_response_msg_list_t);
-
-
-//
-// State for an HTTP/1.x Request+Response exchange, client facing
-//
-typedef struct _client_request_t {
- qdr_http1_request_base_t base;
-
- // The request arrives via the raw connection. These fields are used to
- // build the message and deliver it into the core.
- //
- qd_message_t *request_msg; // holds inbound message as it is built
- qdr_delivery_t *request_dlv; // qdr_link_deliver()
- qd_composed_field_t *request_props; // holds HTTP headers as they arrive
- uint64_t request_dispo; // set by core (core_update_delivery)
- bool request_settled; // set by core (core_update_delivery)
-
- // A single request may result in more than one response (1xx Continue for
- // example). These responses are written to the raw connection from HEAD
- // to TAIL.
- //
- _client_response_msg_list_t responses;
-
- uint32_t error_code;
- char *error_text;
- bool codec_completed; // encoder/decoder done
- bool cancelled;
- bool close_on_complete; // close the conn when this request is complete
- bool conn_close_hdr; // add Connection: close to response msg
- bool expect_continue; // Expect: 100-Continue header found
-
- uint32_t version_major;
- uint32_t version_minor;
-} _client_request_t;
-ALLOC_DECLARE(_client_request_t);
-ALLOC_DEFINE(_client_request_t);
-
-
-static void _client_tx_buffers_cb(h1_codec_request_state_t *lib_hrs, qd_buffer_list_t *blist, unsigned int len);
-static void _client_tx_stream_data_cb(h1_codec_request_state_t *lib_hrs, qd_message_stream_data_t *stream_data);
-static int _client_rx_request_cb(h1_codec_request_state_t *lib_rs,
- const char *method,
- const char *target,
- uint32_t version_major,
- uint32_t version_minor);
-static int _client_rx_response_cb(h1_codec_request_state_t *lib_rs,
- int status_code,
- const char *reason_phrase,
- uint32_t version_major,
- uint32_t version_minor);
-static int _client_rx_header_cb(h1_codec_request_state_t *lib_rs, const char *key, const char *value);
-static int _client_rx_headers_done_cb(h1_codec_request_state_t *lib_rs, bool has_body);
-static int _client_rx_body_cb(h1_codec_request_state_t *lib_rs, qd_buffer_list_t *body, size_t len, bool more);
-static void _client_rx_done_cb(h1_codec_request_state_t *lib_rs);
-static void _client_request_complete_cb(h1_codec_request_state_t *lib_rs, bool cancelled);
-static void _handle_connection_events(pn_event_t *e, qd_server_t *qd_server, void *context);
-static void _client_response_msg_free(_client_request_t *req, _client_response_msg_t *rmsg);
-static void _client_request_free(_client_request_t *req);
-static void _write_pending_response(_client_request_t *req);
-static void _deliver_request(qdr_http1_connection_t *hconn, _client_request_t *req);
-static bool _find_token(const char *list, const char *value);
-
-
-////////////////////////////////////////////////////////
-// HTTP/1.x Client Listener
-////////////////////////////////////////////////////////
-
-
-// Listener received connection request from client
-//
-static qdr_http1_connection_t *_create_client_connection(qd_http_listener_t *li)
-{
- qdr_http1_connection_t *hconn = new_qdr_http1_connection_t();
-
- ZERO(hconn);
- hconn->type = HTTP1_CONN_CLIENT;
- hconn->admin_status = QD_CONN_ADMIN_ENABLED;
- hconn->oper_status = QD_CONN_OPER_DOWN;
- hconn->qd_server = li->server;
- hconn->adaptor = qdr_http1_adaptor;
- hconn->handler_context.handler = &_handle_connection_events;
- hconn->handler_context.context = hconn;
- sys_atomic_init(&hconn->q2_restart, 0);
-
- hconn->client.next_msg_id = 1;
-
- // configure the HTTP/1.x library
-
- h1_codec_config_t config = {0};
- config.type = HTTP1_CONN_CLIENT;
- config.tx_buffers = _client_tx_buffers_cb;
- config.tx_stream_data = _client_tx_stream_data_cb;
- config.rx_request = _client_rx_request_cb;
- config.rx_response = _client_rx_response_cb;
- config.rx_header = _client_rx_header_cb;
- config.rx_headers_done = _client_rx_headers_done_cb;
- config.rx_body = _client_rx_body_cb;
- config.rx_done = _client_rx_done_cb;
- config.request_complete = _client_request_complete_cb;
-
- hconn->http_conn = h1_codec_connection(&config, hconn);
- if (!hconn->http_conn) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_ERROR,
- "Failed to initialize HTTP/1.x library - connection refused.");
- qdr_http1_connection_free(hconn);
- return 0;
- }
-
- hconn->cfg.host = qd_strdup(li->config.host);
- hconn->cfg.port = qd_strdup(li->config.port);
- hconn->cfg.address = qd_strdup(li->config.address);
- hconn->cfg.site = li->config.site ? qd_strdup(li->config.site) : 0;
- hconn->cfg.event_channel = li->config.event_channel;
- hconn->cfg.aggregation = li->config.aggregation;
-
- hconn->raw_conn = pn_raw_connection();
- pn_raw_connection_set_context(hconn->raw_conn, &hconn->handler_context);
-
- sys_mutex_lock(qdr_http1_adaptor->lock);
- DEQ_INSERT_TAIL(qdr_http1_adaptor->connections, hconn);
- sys_mutex_unlock(qdr_http1_adaptor->lock);
-
- // we'll create a QDR connection and links once the raw connection activates
- return hconn;
-}
-
-
-// Process proactor events for the client listener
-//
-static void _handle_listener_events(pn_event_t *e, qd_server_t *qd_server, void *context)
-{
- qd_log_source_t *log = qdr_http1_adaptor->log;
- qd_http_listener_t *li = (qd_http_listener_t*) context;
- const char *host_port = li->config.host_port;
-
- qd_log(log, QD_LOG_DEBUG, "HTTP/1.x Client Listener Event %s\n", pn_event_type_name(pn_event_type(e)));
-
- switch (pn_event_type(e)) {
-
- case PN_LISTENER_OPEN: {
- qd_log(log, QD_LOG_NOTICE, "Listening for HTTP/1.x client requests on %s", host_port);
- break;
- }
-
- case PN_LISTENER_ACCEPT: {
- qd_log(log, QD_LOG_DEBUG, "Accepting HTTP/1.x connection on %s", host_port);
- qdr_http1_connection_t *hconn = _create_client_connection(li);
- if (hconn) {
- // Note: the proactor may schedule the hconn on another thread
- // during this call!
- pn_listener_raw_accept(li->pn_listener, hconn->raw_conn);
- }
- break;
- }
-
- case PN_LISTENER_CLOSE: {
- if (li->pn_listener) {
- pn_condition_t *cond = pn_listener_condition(li->pn_listener);
- if (pn_condition_is_set(cond)) {
- qd_log(log, QD_LOG_ERROR, "Listener error on %s: %s (%s)", host_port,
- pn_condition_get_description(cond),
- pn_condition_get_name(cond));
- } else {
- qd_log(log, QD_LOG_TRACE, "Listener closed on %s", host_port);
- }
-
- sys_mutex_lock(qdr_http1_adaptor->lock);
- pn_listener_set_context(li->pn_listener, 0);
- li->pn_listener = 0;
- DEQ_REMOVE(qdr_http1_adaptor->listeners, li);
- sys_mutex_unlock(qdr_http1_adaptor->lock);
-
- qd_http_listener_decref(li);
- }
- break;
- }
-
- default:
- break;
- }
-}
-
-
-// Management Agent API - Create
-//
-// Note that this runs on the Management Agent thread, which may be running concurrently with the
-// I/O and timer threads.
-//
-qd_http_listener_t *qd_http1_configure_listener(qd_dispatch_t *qd, const qd_http_bridge_config_t *config, qd_entity_t *entity)
-{
- qd_http_listener_t *li = qd_http_listener(qd->server, &_handle_listener_events);
- if (!li) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_ERROR, "Unable to create http listener: no memory");
- return 0;
- }
- li->config = *config;
- DEQ_ITEM_INIT(li);
-
- sys_mutex_lock(qdr_http1_adaptor->lock);
- DEQ_INSERT_TAIL(qdr_http1_adaptor->listeners, li);
- sys_mutex_unlock(qdr_http1_adaptor->lock);
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_INFO, "Configured HTTP_ADAPTOR listener on %s", (&li->config)->host_port);
- // Note: the proactor may schedule the pn_listener on another thread during this call
- pn_proactor_listen(qd_server_proactor(li->server), li->pn_listener, li->config.host_port, LISTENER_BACKLOG);
- return li;
-}
-
-
-// Management Agent API - Delete
-//
-// Note that this runs on the Management Agent thread, which may be running concurrently with the
-// I/O and timer threads.
-//
-void qd_http1_delete_listener(qd_dispatch_t *ignore, qd_http_listener_t *li)
-{
- if (li) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_INFO, "Deleting HttpListener for %s, %s:%s", li->config.address, li->config.host, li->config.port);
- sys_mutex_lock(qdr_http1_adaptor->lock);
- if (li->pn_listener) {
- // note that the proactor may immediately schedule the
- // PN_LISTENER_CLOSED event on another thread...
- pn_listener_close(li->pn_listener);
- }
- sys_mutex_unlock(qdr_http1_adaptor->lock);
- }
-}
-
-
-////////////////////////////////////////////////////////
-// Raw Connector Events
-////////////////////////////////////////////////////////
-
-
-// Raw Connection Initialization
-//
-static void _setup_client_connection(qdr_http1_connection_t *hconn)
-{
- hconn->client.client_ip_addr = qda_raw_conn_get_address(hconn->raw_conn);
- qdr_connection_info_t *info = qdr_connection_info(false, //bool is_encrypted,
- false, //bool is_authenticated,
- true, //bool opened,
- "", //char *sasl_mechanisms,
- QD_INCOMING, //qd_direction_t dir,
- hconn->client.client_ip_addr, //const char *host,
- "", //const char *ssl_proto,
- "", //const char *ssl_cipher,
- "", //const char *user,
- "HTTP/1.x Adaptor", //const char *container,
- 0, //pn_data_t *connection_properties,
- 0, //int ssl_ssf,
- false, //bool ssl,
- "", // peer router version,
- false); // streaming links
-
- hconn->conn_id = qd_server_allocate_connection_id(hconn->qd_server);
- hconn->qdr_conn = qdr_connection_opened(qdr_http1_adaptor->core,
- qdr_http1_adaptor->adaptor,
- true, // incoming
- QDR_ROLE_NORMAL,
- 1, //cost
- hconn->conn_id,
- 0, // label
- 0, // remote container id
- false, // strip annotations in
- false, // strip annotations out
- DEFAULT_CAPACITY,
- 0, // vhost
- 0, // policy_spec
- info,
- 0, // bind context
- 0); // bind token
- qdr_connection_set_context(hconn->qdr_conn, hconn);
- hconn->oper_status = QD_CONN_OPER_UP;
-
- qd_log(hconn->adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"] HTTP connection to client created", hconn->conn_id);
-
- // simulate a client subscription for reply-to
- qdr_terminus_t *dynamic_source = qdr_terminus(0);
- qdr_terminus_set_dynamic(dynamic_source);
- hconn->out_link = qdr_link_first_attach(hconn->qdr_conn,
- QD_OUTGOING,
- dynamic_source, //qdr_terminus_t *source,
- qdr_terminus(0), //qdr_terminus_t *target,
- "http1.client.reply-to", //const char *name,
- 0, //const char *terminus_addr,
- false, // no-route
- NULL, // initial delivery
- &(hconn->out_link_id));
- qdr_link_set_context(hconn->out_link, hconn);
-
- qd_log(hconn->adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] HTTP client response link created",
- hconn->conn_id, hconn->out_link_id);
-
- // simulate a client publisher link to the HTTP server:
- qdr_terminus_t *target = qdr_terminus(0);
- if (hconn->cfg.event_channel) {
- //For an event channel, we always want to be able to handle
- //incoming requests. We use an anonymous publisher so that we
- //get credit regardless of there being consumers.
- qdr_terminus_set_address(target, NULL);
- } else {
- qdr_terminus_set_address(target, hconn->cfg.address);
- }
- hconn->in_link = qdr_link_first_attach(hconn->qdr_conn,
- QD_INCOMING,
- qdr_terminus(0), //qdr_terminus_t *source,
- target, //qdr_terminus_t *target,
- "http1.client.in", //const char *name,
- 0, //const char *terminus_addr,
- false,
- NULL,
- &(hconn->in_link_id));
- qdr_link_set_context(hconn->in_link, hconn);
-
- qd_log(hconn->adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] HTTP client request link created",
- hconn->conn_id, hconn->in_link_id);
-
- // wait until the dynamic reply-to address is returned in the second attach
- // to grant buffers to the raw connection
-}
-
-
-static void _handle_conn_read_close(qdr_http1_connection_t *hconn)
-{
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"] Closed for reading", hconn->conn_id);
-}
-
-
-static void _handle_conn_write_close(qdr_http1_connection_t *hconn)
-{
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"] Closed for writing", hconn->conn_id);
-}
-
-
-// handle PN_RAW_CONNECTION_READ
-static int _handle_conn_read_event(qdr_http1_connection_t *hconn)
-{
- int error = 0;
- qd_buffer_list_t blist;
- uintmax_t length = qdr_http1_get_read_buffers(hconn, &blist);
-
- if (length) {
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Read %"PRIuMAX" bytes from client (%zu buffers)",
- hconn->conn_id, hconn->in_link_id, length, DEQ_SIZE(blist));
-
- if (HTTP1_DUMP_BUFFERS) {
- fprintf(stdout, "\nClient raw buffer READ %"PRIuMAX" total octets\n", length);
- qd_buffer_t *bb = DEQ_HEAD(blist);
- while (bb) {
- fprintf(stdout, " buffer='%.*s'\n", (int)qd_buffer_size(bb), (char*)&bb[1]);
- bb = DEQ_NEXT(bb);
- }
- fflush(stdout);
- }
-
- hconn->in_http1_octets += length;
- error = h1_codec_connection_rx_data(hconn->http_conn, &blist, length);
- }
- return error;
-}
-
-
-// handle PN_RAW_CONNECTION_NEED_READ_BUFFERS
-static void _handle_conn_need_read_buffers(qdr_http1_connection_t *hconn)
-{
- // @TODO(kgiusti): backpressure if no credit
- if (hconn->client.reply_to_addr || hconn->cfg.event_channel /* && hconn->in_link_credit > 0 */) {
- int granted = qdr_http1_grant_read_buffers(hconn);
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"] %d read buffers granted",
- hconn->conn_id, granted);
- }
-}
-
-
-// Proton Connection Event Handler
-//
-static void _handle_connection_events(pn_event_t *e, qd_server_t *qd_server, void *context)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) context;
- qd_log_source_t *log = qdr_http1_adaptor->log;
-
- if (!hconn) return;
-
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] HTTP client proactor event %s", hconn->conn_id, pn_event_type_name(pn_event_type(e)));
-
- switch (pn_event_type(e)) {
-
- case PN_RAW_CONNECTION_CONNECTED: {
- _setup_client_connection(hconn);
-
- const struct pn_netaddr_t *na = pn_raw_connection_remote_addr(hconn->raw_conn);
- if (na) {
- char buf[128];
- if (pn_netaddr_str(na, buf, sizeof(buf)) > 0) {
- qd_log(log, QD_LOG_INFO,
- "[C%"PRIu64"] HTTP/1.x client connection established from %s",
- hconn->conn_id, buf);
- }
- }
- break;
- }
- case PN_RAW_CONNECTION_CLOSED_READ: {
- _handle_conn_read_close(hconn);
- pn_raw_connection_close(hconn->raw_conn);
- break;
- }
- case PN_RAW_CONNECTION_CLOSED_WRITE: {
- _handle_conn_write_close(hconn);
- pn_raw_connection_close(hconn->raw_conn);
- break;
- }
- case PN_RAW_CONNECTION_DISCONNECTED: {
- qd_log(log, QD_LOG_INFO, "[C%"PRIu64"] HTTP/1.x client disconnected", hconn->conn_id);
- pn_raw_connection_set_context(hconn->raw_conn, 0);
-
- // prevent core from waking this connection
- sys_mutex_lock(qdr_http1_adaptor->lock);
- qdr_connection_set_context(hconn->qdr_conn, 0);
- hconn->raw_conn = 0;
- sys_mutex_unlock(qdr_http1_adaptor->lock);
- // at this point the core can no longer activate this connection
-
- hconn->oper_status = QD_CONN_OPER_DOWN;
- if (hconn->out_link) {
- qdr_link_set_context(hconn->out_link, 0);
- qdr_link_detach(hconn->out_link, QD_LOST, 0);
- hconn->out_link = 0;
- }
- if (hconn->in_link) {
- qdr_link_set_context(hconn->in_link, 0);
- qdr_link_detach(hconn->in_link, QD_LOST, 0);
- hconn->in_link = 0;
- }
- if (hconn->qdr_conn) {
- qdr_connection_set_context(hconn->qdr_conn, 0);
- qdr_connection_closed(hconn->qdr_conn);
- hconn->qdr_conn = 0;
- }
-
- qdr_http1_connection_free(hconn);
- return; // hconn no longer valid
- }
- case PN_RAW_CONNECTION_NEED_WRITE_BUFFERS: {
- qd_log(log, QD_LOG_DEBUG, "[C%"PRIu64"] Need write buffers", hconn->conn_id);
- _write_pending_response((_client_request_t*) DEQ_HEAD(hconn->requests));
- break;
- }
- case PN_RAW_CONNECTION_WRITTEN: {
- qdr_http1_free_written_buffers(hconn);
- break;
- }
- case PN_RAW_CONNECTION_NEED_READ_BUFFERS: {
- qd_log(log, QD_LOG_DEBUG, "[C%"PRIu64"] Need read buffers", hconn->conn_id);
- _handle_conn_need_read_buffers(hconn);
- break;
- }
- case PN_RAW_CONNECTION_READ: {
- if (!hconn->q2_blocked) {
- int error = _handle_conn_read_event(hconn);
- if (error)
- qdr_http1_close_connection(hconn, "Incoming response message failed to parse");
- else
- // room for more incoming data
- _handle_conn_need_read_buffers(hconn);
- }
- break;
- }
- case PN_RAW_CONNECTION_WAKE: {
- int error = 0;
- qd_log(log, QD_LOG_DEBUG, "[C%"PRIu64"] Wake-up", hconn->conn_id);
-
- if (sys_atomic_set(&hconn->q2_restart, 0)) {
- // note: unit tests grep for this log!
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] client link unblocked from Q2 limit", hconn->conn_id);
- hconn->q2_blocked = false;
- error = _handle_conn_read_event(hconn); // restart receiving
- if (!error)
- // room for more incoming data
- _handle_conn_need_read_buffers(hconn);
- }
-
- while (qdr_connection_process(hconn->qdr_conn)) {}
-
- if (error)
- qdr_http1_close_connection(hconn, "Incoming request message failed to parse");
-
- qd_log(log, QD_LOG_DEBUG, "[C%"PRIu64"] Processing done", hconn->conn_id);
- break;
- }
- default:
- break;
- }
-
- // Check the head request for completion and advance to next request if
- // done.
-
- // remove me:
- if (hconn) {
- _client_request_t *hreq = (_client_request_t*) DEQ_HEAD(hconn->requests);
- if (hreq) {
- qd_log(log, QD_LOG_DEBUG, "[C%"PRIu64"] HTTP is client request msg-id=%"PRIu64" complete????",
- hconn->conn_id, hreq->base.msg_id);
- qd_log(log, QD_LOG_DEBUG, " codec=%s req-dlv=%p resp-dlv=%d req_msg=%p %s",
- hreq->codec_completed ? "Done" : "Not Done",
- (void*)hreq->request_dlv,
- (int)DEQ_SIZE(hreq->responses),
- (void*)hreq->request_msg,
- hreq->cancelled ? "Cancelled" : "Not Cancelled");
- }
- }
-
- // check if the head request is done
-
- bool need_close = false;
- _client_request_t *hreq = (_client_request_t *)DEQ_HEAD(hconn->requests);
- if (hreq) {
- if (hreq->cancelled) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP client request msg-id=%"PRIu64" cancelled",
- hconn->conn_id, hconn->out_link_id, hreq->base.msg_id);
- need_close = true;
- } else {
- if (hreq->error_code) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE, "[C%"PRIu64"][L%"PRIu64"] Responding with %i %s", hconn->conn_id,
- hconn->out_link_id, hreq->error_code, hreq->error_text);
- _client_response_msg_t *rmsg = new__client_response_msg_t();
- ZERO(rmsg);
- DEQ_INIT(rmsg->out_data);
- DEQ_INSERT_TAIL(hreq->responses, rmsg);
- qdr_http1_error_response(&hreq->base, hreq->error_code, hreq->error_text);
- _write_pending_response(hreq);
- }
- // Can we retire the current outgoing response messages?
- //
- _client_response_msg_t *rmsg = DEQ_HEAD(hreq->responses);
- while (rmsg &&
- rmsg->dispo &&
- DEQ_IS_EMPTY(rmsg->out_data) &&
- hconn->cfg.aggregation == QD_AGGREGATION_NONE) {
- // response message fully received and forwarded to client
- if (rmsg->dlv) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP client request msg-id=%"PRIu64" settling response, dispo=0x%"PRIx64,
- hconn->conn_id, hconn->out_link_id, hreq->base.msg_id, rmsg->dispo);
- qdr_delivery_remote_state_updated(qdr_http1_adaptor->core,
- rmsg->dlv,
- rmsg->dispo,
- true, // settled,
- 0, // delivery state
- false);
- }
- qdr_link_flow(qdr_http1_adaptor->core, hconn->out_link, 1, false);
- _client_response_msg_free(hreq, rmsg);
- rmsg = DEQ_HEAD(hreq->responses);
- }
-
- if (hreq->codec_completed &&
- DEQ_IS_EMPTY(hreq->responses) &&
- hreq->request_settled) {
-
- qd_log(log, QD_LOG_DEBUG, "[C%"PRIu64"] HTTP request msg-id=%"PRIu64" completed!",
- hconn->conn_id, hreq->base.msg_id);
-
- need_close = hreq->close_on_complete;
- _client_request_free(hreq);
- }
- }
- }
-
- if (need_close)
- qdr_http1_close_connection(hconn, 0);
- else {
- hreq = (_client_request_t*) DEQ_HEAD(hconn->requests);
- if (hreq) {
-
- if (hreq->request_msg && hconn->in_link_credit > 0) {
-
- qd_log(hconn->adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Delivering next request msg-id=%"PRIu64" to router",
- hconn->conn_id, hconn->in_link_id, hreq->base.msg_id);
-
- hconn->in_link_credit -= 1;
- _deliver_request(hconn, hreq);
- }
-
- _write_pending_response(hreq);
- }
- }
-}
-
-
-
-
-////////////////////////////////////////////////////////
-// HTTP/1.x Encoder/Decoder Callbacks
-////////////////////////////////////////////////////////
-
-
-// Encoder callback: send blist buffers (response msg) to client endpoint
-//
-static void _client_tx_buffers_cb(h1_codec_request_state_t *hrs, qd_buffer_list_t *blist, unsigned int len)
-{
- _client_request_t *hreq = (_client_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- if (!hconn->raw_conn) {
- // client connection has been lost
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"] Discarding outgoing data - client connection closed", hconn->conn_id);
- qd_buffer_list_free_buffers(blist);
- return;
- }
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] %u response octets encoded",
- hconn->conn_id, hconn->out_link_id, len);
-
-
- _client_response_msg_t *rmsg;
- if (hconn->cfg.aggregation == QD_AGGREGATION_NONE) {
- // responses are decoded one at a time - the current response it at the
- // tail of the response list
- rmsg = DEQ_TAIL(hreq->responses);
- } else {
- // when responses are aggregated the buffers don't need to be
- // correlated to specific responses as they will all be
- // written out together, so can just use the head of the
- // response list
- rmsg = DEQ_HEAD(hreq->responses);
- }
- assert(rmsg);
- qdr_http1_enqueue_buffer_list(&rmsg->out_data, blist, len);
-
- // if this happens to be the current outgoing response try writing to the
- // raw connection
-
- if (rmsg == DEQ_HEAD(hreq->responses))
- _write_pending_response(hreq);
-}
-
-
-// Encoder callback: send stream_data buffers (response msg) to client endpoint
-//
-static void _client_tx_stream_data_cb(h1_codec_request_state_t *hrs, qd_message_stream_data_t *stream_data)
-{
- _client_request_t *hreq = (_client_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- if (!hconn->raw_conn) {
- // client connection has been lost
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"] Discarding outgoing data - client connection closed", hconn->conn_id);
- qd_message_stream_data_release(stream_data);
- return;
- }
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Sending body data to client",
- hconn->conn_id, hconn->out_link_id);
-
-
- _client_response_msg_t *rmsg;
- if (hconn->cfg.aggregation == QD_AGGREGATION_NONE) {
- // responses are decoded one at a time - the current response it at the
- // tail of the response list
- rmsg = DEQ_TAIL(hreq->responses);
- } else {
- // when responses are aggregated the buffers don't need to be
- // correlated to specific responses as they will all be
- // written out together, so can just use the head of the
- // response list
- rmsg = DEQ_HEAD(hreq->responses);
- }
- assert(rmsg);
- qdr_http1_enqueue_stream_data(&rmsg->out_data, stream_data);
-
- // if this happens to be the current outgoing response try writing to the
- // raw connection
-
- if (rmsg == DEQ_HEAD(hreq->responses))
- _write_pending_response(hreq);
-}
-
-
-// Called when decoding an HTTP request from a client. This indicates the
-// start of a new request message.
-//
-static int _client_rx_request_cb(h1_codec_request_state_t *hrs,
- const char *method,
- const char *target,
- uint32_t version_major,
- uint32_t version_minor)
-{
- h1_codec_connection_t *h1c = h1_codec_request_state_get_connection(hrs);
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*)h1_codec_connection_get_context(h1c);
-
- _client_request_t *creq = new__client_request_t();
- ZERO(creq);
- creq->base.start = qd_timer_now();
- creq->base.msg_id = hconn->client.next_msg_id++;
- creq->base.lib_rs = hrs;
- creq->base.hconn = hconn;
- creq->close_on_complete = (version_minor == 0);
- creq->version_major = version_major;
- creq->version_minor = version_minor;
- DEQ_INIT(creq->responses);
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"] HTTP request received: msg-id=%"PRIu64" method=%s target=%s version=%"PRIi32".%"PRIi32,
- hconn->conn_id, creq->base.msg_id, method, target, version_major, version_minor);
- if (hconn->cfg.event_channel) {
- if (strcasecmp(method, POST_METHOD) == 0) {
- creq->error_code = 204;
- creq->error_text = "Event posted.";
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"] Event posted", hconn->conn_id);
- } else {
- creq->error_code = 405;
- creq->error_text = "Invalid method for event channel, only POST is allowed.";
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING, "[C%"PRIu64"] HTTP %s request not allowed for event channel", hconn->conn_id, method);
- }
- }
-
- creq->request_props = qd_compose(QD_PERFORMATIVE_APPLICATION_PROPERTIES, 0);
- qd_compose_start_map(creq->request_props);
- {
- // OASIS specifies this value as "1.1" by default...
- char temp[64];
- snprintf(temp, sizeof(temp), "%"PRIi32".%"PRIi32, version_major, version_minor);
- qd_compose_insert_string(creq->request_props, VERSION_PROP_KEY);
- qd_compose_insert_string(creq->request_props, temp);
-
- qd_compose_insert_string(creq->request_props, PATH_PROP_KEY);
- qd_compose_insert_string(creq->request_props, target);
- }
-
- h1_codec_request_state_set_context(hrs, (void*) creq);
- DEQ_INSERT_TAIL(hconn->requests, &creq->base);
- return 0;
-}
-
-
-// Cannot happen for a client connection!
-static int _client_rx_response_cb(h1_codec_request_state_t *hrs,
- int status_code,
- const char *reason_phrase,
- uint32_t version_major,
- uint32_t version_minor)
-{
- _client_request_t *hreq = (_client_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_ERROR,
- "[C%"PRIu64"][L%"PRIu64"] Spurious HTTP response received from client",
- hconn->conn_id, hconn->in_link_id);
- return HTTP1_STATUS_BAD_REQ;
-}
-
-
-// called for each decoded HTTP header.
-//
-static int _client_rx_header_cb(h1_codec_request_state_t *hrs, const char *key, const char *value)
-{
- _client_request_t *hreq = (_client_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP request header received: key='%s' value='%s'",
- hconn->conn_id, hconn->in_link_id, key, value);
-
- if (strcasecmp(key, "Connection") == 0) {
- // We need to filter the connection header out. But first see if
- // client requested that the connection be closed after the response
- // arrives.
- //
- // @TODO(kgiusti): also have to remove other headers given in value!
- // @TODO(kgiusti): do we need to support keep-alive on 1.0 connections?
- //
- if (_find_token(value, "close")) {
- hreq->close_on_complete = true;
- hreq->conn_close_hdr = true;
- }
-
- } else {
- if (strcasecmp(key, "Expect") == 0) {
- // DISPATCH-2189: mark this message as streaming so the router will
- // not wait for it to complete before forwarding it.
- hreq->expect_continue = _find_token(value, "100-continue");
- }
- qd_compose_insert_symbol(hreq->request_props, key);
- qd_compose_insert_string(hreq->request_props, value);
- }
-
- return 0;
-}
-
-
-// Called after the last header is decoded, before decoding any body data.
-// At this point there is enough data to start forwarding the message to
-// the router.
-//
-static int _client_rx_headers_done_cb(h1_codec_request_state_t *hrs, bool has_body)
-{
- _client_request_t *hreq = (_client_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- if (hconn->cfg.event_channel && strcasecmp(h1_codec_request_state_method(hrs), POST_METHOD) != 0) {
- return 0;
- }
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP request headers done.",
- hconn->conn_id, hconn->in_link_id);
-
- // now that all the headers have been received we can construct
- // the AMQP message
-
- hreq->request_msg = qd_message();
- qd_message_set_stream_annotation(hreq->request_msg, hreq->expect_continue);
- qdr_new_message_annotate(hreq->base.hconn->adaptor->core, hreq->request_msg);
-
- qd_composed_field_t *hdrs = qd_compose(QD_PERFORMATIVE_HEADER, 0);
- qd_compose_start_list(hdrs);
- qd_compose_insert_bool(hdrs, 0); // durable
- qd_compose_insert_null(hdrs); // priority
- //qd_compose_insert_null(hdrs); // ttl
- //qd_compose_insert_bool(hdrs, 0); // first-acquirer
- //qd_compose_insert_uint(hdrs, 0); // delivery-count
- qd_compose_end_list(hdrs);
-
- qd_composed_field_t *props = qd_compose(QD_PERFORMATIVE_PROPERTIES, hdrs);
- qd_compose_start_list(props);
-
- qd_compose_insert_ulong(props, hreq->base.msg_id); // message-id
- qd_compose_insert_null(props); // user-id
-
- qd_compose_insert_string(props, hconn->cfg.address); // to
- qd_compose_insert_string(props, h1_codec_request_state_method(hrs)); // subject
- if (hconn->cfg.event_channel) {
- // event channel does not want replies
- qd_compose_insert_null(props); // reply-to
- } else {
- qd_compose_insert_string(props, hconn->client.reply_to_addr); // reply-to
- }
- qd_compose_insert_null(props); // correlation-id
- qd_compose_insert_null(props); // content-type
- qd_compose_insert_null(props); // content-encoding
- qd_compose_insert_null(props); // absolute-expiry-time
- qd_compose_insert_null(props); // creation-time
- qd_compose_insert_string(props, hconn->cfg.site); // group-id
-
- qd_compose_end_list(props);
-
- qd_compose_end_map(hreq->request_props);
-
- qd_message_compose_3(hreq->request_msg, props, hreq->request_props, !has_body);
- qd_compose_free(props);
- qd_compose_free(hreq->request_props);
- hreq->request_props = 0;
-
- // future-proof: ensure the message headers have not caused Q2
- // blocking. We only check for Q2 events while adding body data.
- assert(!qd_message_is_Q2_blocked(hreq->request_msg));
-
- qd_alloc_safe_ptr_t hconn_sp = QD_SAFE_PTR_INIT(hconn);
- qd_message_set_q2_unblocked_handler(hreq->request_msg, qdr_http1_q2_unblocked_handler, hconn_sp);
-
- // Use up one credit to obtain a delivery and forward to core. If no
- // credit is available the request is stalled until the core grants more
- // flow.
- if (hreq == (_client_request_t*) DEQ_HEAD(hconn->requests) && hconn->in_link_credit > 0) {
- hconn->in_link_credit -= 1;
-
- qd_log(hconn->adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Delivering request msg-id=%"PRIu64" to router",
- hconn->conn_id, hconn->in_link_id, hreq->base.msg_id);
-
- _deliver_request(hconn, hreq);
- }
-
- return 0;
-}
-
-
-// Called with decoded body data. This may be called multiple times as body
-// data becomes available.
-//
-static int _client_rx_body_cb(h1_codec_request_state_t *hrs, qd_buffer_list_t *body, size_t len,
- bool more)
-{
- bool q2_blocked = false;
- _client_request_t *hreq = (_client_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
- if (hconn->cfg.event_channel && strcasecmp(h1_codec_request_state_method(hrs), POST_METHOD) != 0) {
- qd_buffer_list_free_buffers(body);
- return 0;
- }
- qd_message_t *msg = hreq->request_msg ? hreq->request_msg : qdr_delivery_message(hreq->request_dlv);
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP request body received len=%zu.",
- hconn->conn_id, hconn->in_link_id, len);
-
- qd_message_stream_data_append(msg, body, &q2_blocked);
- hconn->q2_blocked = hconn->q2_blocked || q2_blocked;
- if (q2_blocked) {
- // note: unit tests grep for this log!
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE, "[C%"PRIu64"] client link blocked on Q2 limit", hconn->conn_id);
- }
-
- //
- // Notify the router that more data is ready to be pushed out on the delivery
- //
- if (!more)
- qd_message_set_receive_complete(msg);
-
- if (hreq->request_dlv)
- qdr_delivery_continue(qdr_http1_adaptor->core, hreq->request_dlv, false);
-
- return 0;
-}
-
-
-// Called at the completion of request message decoding.
-//
-static void _client_rx_done_cb(h1_codec_request_state_t *hrs)
-{
- _client_request_t *hreq = (_client_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
- qd_message_t *msg = hreq->request_msg ? hreq->request_msg : qdr_delivery_message(hreq->request_dlv);
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP request msg-id=%"PRIu64" receive complete.",
- hconn->conn_id, hconn->in_link_id, hreq->base.msg_id);
-
- if (!qd_message_receive_complete(msg)) {
- qd_message_set_receive_complete(msg);
- if (hreq->request_dlv) {
- qdr_delivery_continue(qdr_http1_adaptor->core, hreq->request_dlv, false);
- }
- }
-}
-
-
-// The codec has completed processing the request and response messages.
-//
-static void _client_request_complete_cb(h1_codec_request_state_t *lib_rs, bool cancelled)
-{
- _client_request_t *hreq = (_client_request_t*) h1_codec_request_state_get_context(lib_rs);
- if (hreq) {
- hreq->base.stop = qd_timer_now();
- qdr_http1_record_client_request_info(qdr_http1_adaptor, &hreq->base);
- hreq->base.lib_rs = 0; // freed on return from this call
- hreq->cancelled = hreq->cancelled || cancelled;
- hreq->codec_completed = !hreq->cancelled;
-
- uint64_t in_octets, out_octets;
- h1_codec_request_state_counters(lib_rs, &in_octets, &out_octets);
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"] HTTP request msg-id=%"PRIu64" %s. Octets read: %"PRIu64" written: %"PRIu64,
- hreq->base.hconn->conn_id,
- hreq->base.msg_id,
- cancelled ? "cancelled" : "codec done",
- in_octets, out_octets);
- }
-}
-
-
-//////////////////////////////////////////////////////////////////////
-// Router Protocol Adapter Callbacks
-//////////////////////////////////////////////////////////////////////
-
-
-void qdr_http1_client_core_second_attach(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_link_t *link,
- qdr_terminus_t *source,
- qdr_terminus_t *target)
-{
- if (link == hconn->out_link) {
- // this is the reply-to link for the client
- qd_iterator_t *reply_iter = qdr_terminus_get_address(source);
- hconn->client.reply_to_addr = (char*) qd_iterator_copy(reply_iter);
-
- assert(hconn->client.reply_to_addr);
-
- hconn->out_link_credit += DEFAULT_CAPACITY;
- qdr_link_flow(adaptor->core, link, DEFAULT_CAPACITY, false);
- }
-}
-
-
-void qdr_http1_client_core_link_flow(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_link_t *link,
- int credit)
-{
- qd_log(adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Credit granted on request link %d",
- hconn->conn_id, hconn->in_link_id, credit);
-
- assert(link == hconn->in_link); // router only grants flow on incoming link
-
- hconn->in_link_credit += credit;
- if (hconn->in_link_credit > 0) {
-
- _handle_conn_need_read_buffers(hconn);
-
- // is the current request message blocked by lack of credit?
-
- _client_request_t *hreq = (_client_request_t *)DEQ_HEAD(hconn->requests);
- if (hreq && hreq->request_msg) {
- assert(!hreq->request_dlv);
- hconn->in_link_credit -= 1;
-
- qd_log(hconn->adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Delivering next request msg-id=%"PRIu64" to router",
- hconn->conn_id, hconn->in_link_id, hreq->base.msg_id);
-
- _deliver_request(hconn, hreq);
- }
- }
-}
-
-static bool _get_multipart_content_length(_client_request_t *hreq, char *value)
-{
- uint64_t total = 0;
- for (_client_response_msg_t *rmsg = DEQ_HEAD(hreq->responses); rmsg; rmsg = rmsg->next) {
- qd_message_t *msg = qdr_delivery_message(rmsg->dlv);
- uint64_t content_length = h1_codec_tx_multipart_section_boundary_length();
- bool got_body_length = false;
-
- qd_iterator_t *app_props_iter = qd_message_field_iterator(msg, QD_FIELD_APPLICATION_PROPERTIES);
- if (app_props_iter) {
- qd_parsed_field_t *app_props = qd_parse(app_props_iter);
- if (app_props && qd_parse_is_map(app_props)) {
- // now send all headers in app properties
- qd_parsed_field_t *key = qd_field_first_child(app_props);
- while (key) {
- qd_parsed_field_t *value = qd_field_next_child(key);
- if (!value)
- break;
-
- qd_iterator_t *i_key = qd_parse_raw(key);
- if (!i_key)
- break;
-
- if (qd_iterator_equal(i_key, (const unsigned char*) CONTENT_LENGTH_KEY)) {
- qd_iterator_t *i_value = qd_parse_raw(value);
- if (i_value) {
- char *length_str = (char*) qd_iterator_copy(i_value);
- uint64_t body_length;
- sscanf(length_str, "%"SCNu64, &body_length);
- free(length_str);
- got_body_length = true;
- content_length += body_length;
- }
- } else if (!qd_iterator_prefix(i_key, ":")) {
- // ignore meta-data headers
- qd_iterator_t *i_value = qd_parse_raw(value);
- if (!i_value)
- break;
-
- content_length += qd_iterator_length(i_key) + 2 + qd_iterator_length(i_value) + 2;
- }
-
- key = qd_field_next_child(value);
- }
- }
- qd_parse_free(app_props);
- }
- qd_iterator_free(app_props_iter);
- if (got_body_length) {
- total += content_length;
- } else {
- return false;
- }
- }
- total += h1_codec_tx_multipart_end_boundary_length();
- sprintf(value, "%"SCNu64, total);
- return true;
-}
-
-static void _encode_json_response(_client_request_t *hreq)
-{
- qdr_http1_connection_t *hconn = hreq->base.hconn;
- qd_log(hconn->adaptor->log, QD_LOG_TRACE, "[C%"PRIu64"] encoding json response", hconn->conn_id);
- bool ok = !h1_codec_tx_response(hreq->base.lib_rs, 200, NULL, hreq->version_major, hreq->version_minor);
- if (!ok) {
- qd_log(hconn->adaptor->log, QD_LOG_TRACE, "[C%"PRIu64"] Could not encode response", hconn->conn_id);
- return;
- }
- PyObject* msgs = 0;
- qd_json_msgs_init(&msgs);
- for (_client_response_msg_t *rmsg = DEQ_HEAD(hreq->responses); rmsg; rmsg = rmsg->next) {
- qd_message_t *msg = qdr_delivery_message(rmsg->dlv);
- qd_json_msgs_append(msgs, msg);
- rmsg->encoded = true;
- }
- char *body = qd_json_msgs_string(msgs);
- if (body) {
- h1_codec_tx_add_header(hreq->base.lib_rs, "Content-Type", "application/json");
- int len = strlen(body);
- char content_length[25];
- sprintf(content_length, "%i", len);
- h1_codec_tx_add_header(hreq->base.lib_rs, CONTENT_LENGTH_KEY, content_length);
- h1_codec_tx_body_str(hreq->base.lib_rs, body);
- free(body);
- } else {
- qd_log(hconn->adaptor->log, QD_LOG_ERROR, "[C%"PRIu64"] No aggregated json response returned", hconn->conn_id);
- }
- bool need_close;
- h1_codec_tx_done(hreq->base.lib_rs, &need_close);
- hreq->close_on_complete = need_close || hreq->close_on_complete;
- hreq->codec_completed = true;
-}
-
-static void _encode_multipart_response(_client_request_t *hreq)
-{
- qdr_http1_connection_t *hconn = hreq->base.hconn;
- qd_log(hconn->adaptor->log, QD_LOG_TRACE, "[C%"PRIu64"] encoding multipart response", hconn->conn_id);
- bool ok = !h1_codec_tx_response(hreq->base.lib_rs, 200, NULL, hreq->version_major, hreq->version_minor);
- char content_length[25];
- if (_get_multipart_content_length(hreq, content_length)) {
- h1_codec_tx_add_header(hreq->base.lib_rs, CONTENT_LENGTH_KEY, content_length);
- }
- h1_codec_tx_begin_multipart(hreq->base.lib_rs);
- for (_client_response_msg_t *rmsg = DEQ_HEAD(hreq->responses); rmsg; rmsg = rmsg->next) {
- h1_codec_tx_begin_multipart_section(hreq->base.lib_rs);
- qd_message_t *msg = qdr_delivery_message(rmsg->dlv);
-
- qd_iterator_t *app_props_iter = qd_message_field_iterator(msg, QD_FIELD_APPLICATION_PROPERTIES);
- if (app_props_iter) {
- qd_parsed_field_t *app_props = qd_parse(app_props_iter);
- if (app_props && qd_parse_is_map(app_props)) {
- // now send all headers in app properties
- qd_parsed_field_t *key = qd_field_first_child(app_props);
- while (ok && key) {
- qd_parsed_field_t *value = qd_field_next_child(key);
- if (!value)
- break;
-
- qd_iterator_t *i_key = qd_parse_raw(key);
- if (!i_key)
- break;
-
- // ignore the special headers added by the mapping and
- // content-length field (TODO: case insensitive comparison
- // for content-length)
- if (!qd_iterator_prefix(i_key, ":")
- && !qd_iterator_equal(i_key, (const unsigned char*) CONTENT_LENGTH_KEY)) {
-
- qd_iterator_t *i_value = qd_parse_raw(value);
- if (!i_value)
- break;
-
- char *header_key = (char*) qd_iterator_copy(i_key);
- char *header_value = (char*) qd_iterator_copy(i_value);
- ok = !h1_codec_tx_add_header(hreq->base.lib_rs, header_key, header_value);
-
- free(header_key);
- free(header_value);
- }
-
- key = qd_field_next_child(value);
- }
- }
- qd_parse_free(app_props);
- }
- qd_iterator_free(app_props_iter);
- rmsg->headers_encoded = true;
-
- qd_message_stream_data_t *body_data = 0;
- bool done = false;
- while (ok && !done) {
- switch (qd_message_next_stream_data(msg, &body_data)) {
-
- case QD_MESSAGE_STREAM_DATA_BODY_OK:
-
- qd_log(hconn->adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Encoding response body data",
- hconn->conn_id, hconn->out_link_id);
-
- if (h1_codec_tx_body(hreq->base.lib_rs, body_data)) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] body data encode failed",
- hconn->conn_id, hconn->out_link_id);
- ok = false;
- }
- break;
-
- case QD_MESSAGE_STREAM_DATA_NO_MORE:
- // indicate this message is complete
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] response message encoding completed",
- hconn->conn_id, hconn->out_link_id);
- done = true;
- break;
-
- case QD_MESSAGE_STREAM_DATA_INCOMPLETE:
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] Ignoring incomplete body data in aggregated response.",
- hconn->conn_id, hconn->out_link_id);
- done = true;
- break; // wait for more
-
- case QD_MESSAGE_STREAM_DATA_INVALID:
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] Ignoring corrupted body data in aggregated response.",
- hconn->conn_id, hconn->out_link_id);
- done = true;
- break;
-
- case QD_MESSAGE_STREAM_DATA_FOOTER_OK:
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] Ignoring footer in aggregated response.",
- hconn->conn_id, hconn->out_link_id);
- done = true;
- break;
- }
- }
- rmsg->encoded = true;
-
- }
- h1_codec_tx_end_multipart(hreq->base.lib_rs);
- bool need_close;
- h1_codec_tx_done(hreq->base.lib_rs, &need_close);
- hreq->close_on_complete = need_close || hreq->close_on_complete;
- hreq->codec_completed = true;
-}
-
-static void _encode_aggregated_response(qdr_http1_connection_t *hconn, _client_request_t *hreq)
-{
- if (hconn->cfg.aggregation == QD_AGGREGATION_MULTIPART) {
- _encode_multipart_response(hreq);
- } else if (hconn->cfg.aggregation == QD_AGGREGATION_JSON) {
- _encode_json_response(hreq);
- }
-}
-
-static void _encode_empty_response(qdr_http1_connection_t *hconn, _client_request_t *hreq)
-{
- qd_log(hconn->adaptor->log, QD_LOG_TRACE, "[C%"PRIu64"] encoding empty response", hconn->conn_id);
- h1_codec_tx_response(hreq->base.lib_rs, 204, NULL, hreq->version_major, hreq->version_minor);
- bool need_close;
- h1_codec_tx_done(hreq->base.lib_rs, &need_close);
- hreq->close_on_complete = need_close || hreq->close_on_complete;
- hreq->codec_completed = true;
-}
-
-// Handle disposition/settlement update for the outstanding request msg
-//
-void qdr_http1_client_core_delivery_update(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_http1_request_base_t *req,
- qdr_delivery_t *dlv,
- uint64_t disp,
- bool settled)
-{
- _client_request_t *hreq = (_client_request_t *)req;
- assert(dlv == hreq->request_dlv);
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP request msg-id=%"PRIu64" delivery update, outcome=0x%"PRIx64"%s",
- hconn->conn_id, hconn->in_link_id, hreq->base.msg_id, disp, settled ? " settled" : "");
-
- if (disp && disp != PN_RECEIVED && hreq->request_dispo == 0) {
- // terminal disposition
- hreq->request_dispo = disp;
- if (hconn->cfg.aggregation != QD_AGGREGATION_NONE) {
- // when aggregating response from a multicast request, the
- // acknowledgement of the request triggers generating the
- // output from the responses received
- if (settled) {
- if (DEQ_IS_EMPTY(hreq->responses)) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Aggregation request settled but no responses received.", hconn->conn_id, hconn->in_link_id);
- _encode_empty_response(hconn, hreq);
- } else {
- _encode_aggregated_response(hconn, hreq);
- }
- _write_pending_response(hreq);
- }
- } else if (disp != PN_ACCEPTED) {
- // no response message is going to arrive. Now what? For now fake
- // a response from the server by using the codec to write an error
- // response on the behalf of the server.
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] HTTP request failure, outcome=0x%"PRIx64,
- hconn->conn_id, hconn->in_link_id, disp);
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] HTTP request msg-id=%"PRIu64" failure, outcome=0x%"PRIx64,
- hconn->conn_id, hconn->in_link_id, hreq->base.msg_id, disp);
-
- if (hreq->base.out_http1_octets == 0) {
- // best effort attempt to send an error to the client
- // if nothing has been sent back so far
- _client_response_msg_t *rmsg = new__client_response_msg_t();
- ZERO(rmsg);
- DEQ_INIT(rmsg->out_data);
- DEQ_INSERT_TAIL(hreq->responses, rmsg);
-
- if (disp == PN_REJECTED) {
- qdr_http1_error_response(&hreq->base, 400, "Bad Request");
- } else {
- // total guess as to what the proper error code should be
- qdr_http1_error_response(&hreq->base, 503, "Service Unavailable");
- }
- hreq->close_on_complete = true; // trust nothing
-
- } else {
- // partial response already sent - punt:
- qdr_http1_close_connection(hconn, "HTTP request failed");
- }
- }
- }
-
- hreq->request_settled = settled || hreq->request_settled;
-}
-
-
-//
-// Response message forwarding
-//
-
-
-// use the correlation ID from the AMQP message containing the response to look
-// up the original request context
-//
-static _client_request_t *_lookup_request_context(qdr_http1_connection_t *hconn,
- qd_message_t *msg)
-{
- qdr_http1_request_base_t *req = 0;
-
- qd_parsed_field_t *cid_pf = 0;
- qd_iterator_t *cid_iter = qd_message_field_iterator_typed(msg, QD_FIELD_CORRELATION_ID);
- if (cid_iter) {
- cid_pf = qd_parse(cid_iter);
- if (cid_pf && qd_parse_ok(cid_pf)) {
- uint64_t cid = qd_parse_as_ulong(cid_pf);
- if (qd_parse_ok(cid_pf)) {
- req = DEQ_HEAD(hconn->requests);
- while (req) {
- if (req->msg_id == cid)
- break;
- req = DEQ_NEXT(req);
- }
- }
- }
- }
-
- qd_parse_free(cid_pf);
- qd_iterator_free(cid_iter);
-
- return (_client_request_t*) req;
-}
-
-
-// Encode the response status and all HTTP headers.
-// The message has been validated to app properties depth
-//
-static bool _encode_response_headers(_client_request_t *hreq,
- _client_response_msg_t *rmsg)
-{
- bool ok = false;
- qd_message_t *msg = qdr_delivery_message(rmsg->dlv);
-
- if (!hreq->base.site) {
- qd_iterator_t *group_id_itr = qd_message_field_iterator(msg, QD_FIELD_GROUP_ID);
- hreq->base.site = (char*) qd_iterator_copy(group_id_itr);
- qd_iterator_free(group_id_itr);
- }
-
- uint32_t status_code = 0;
- qd_iterator_t *subject_iter = qd_message_field_iterator(msg, QD_FIELD_SUBJECT);
- if (subject_iter) {
- char *status_str = (char*) qd_iterator_copy(subject_iter);
- if (status_str && sscanf(status_str, "%"PRIu32, &status_code) == 1) {
-
- qd_iterator_t *app_props_iter = qd_message_field_iterator(msg,
- QD_FIELD_APPLICATION_PROPERTIES);
- if (app_props_iter) {
- qd_parsed_field_t *app_props = qd_parse(app_props_iter);
- if (app_props && qd_parse_is_map(app_props)) {
-
- // the value for RESPONSE_HEADER_KEY is optional and is set
- // to a string representation of the version of the server
- // (e.g. "1.1")
- uint32_t major = 1;
- uint32_t minor = 1;
- qd_parsed_field_t *tmp = qd_parse_value_by_key(app_props, VERSION_PROP_KEY);
- if (tmp) {
- char *version_str = (char*) qd_iterator_copy(qd_parse_raw(tmp));
- if (version_str) {
- sscanf(version_str, "%"SCNu32".%"SCNu32, &major, &minor);
- free(version_str);
- }
- }
- char *reason_str = 0;
- tmp = qd_parse_value_by_key(app_props, REASON_PROP_KEY);
- if (tmp) {
- reason_str = (char*) qd_iterator_copy(qd_parse_raw(tmp));
- }
-
- qd_log(hreq->base.hconn->adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Encoding response %d %s",
- hreq->base.hconn->conn_id, hreq->base.hconn->out_link_id, (int)status_code,
- reason_str ? reason_str : "");
-
- ok = !h1_codec_tx_response(hreq->base.lib_rs, (int)status_code, reason_str, major, minor);
- free(reason_str);
-
- // now send all headers in app properties
- qd_parsed_field_t *key = qd_field_first_child(app_props);
- while (ok && key) {
- qd_parsed_field_t *value = qd_field_next_child(key);
- if (!value)
- break;
-
- qd_iterator_t *i_key = qd_parse_raw(key);
- if (!i_key)
- break;
-
- // ignore the special headers added by the mapping
- if (!qd_iterator_prefix(i_key, ":")) {
-
- qd_iterator_t *i_value = qd_parse_raw(value);
- if (!i_value)
- break;
-
- char *header_key = (char*) qd_iterator_copy(i_key);
- char *header_value = (char*) qd_iterator_copy(i_value);
-
- // @TODO(kgiusti): remove me (sensitive content)
- qd_log(hreq->base.hconn->adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Encoding response header %s:%s",
- hreq->base.hconn->conn_id, hreq->base.hconn->out_link_id,
- header_key, header_value);
-
- ok = !h1_codec_tx_add_header(hreq->base.lib_rs, header_key, header_value);
-
- free(header_key);
- free(header_value);
- }
-
- key = qd_field_next_child(value);
- }
-
- // If the client has requested Connection: close respond
- // accordingly IF this is the terminal response (not
- // INFORMATIONAL)
- if (ok && (status_code / 100) == 1) {
- if (hreq->conn_close_hdr) {
- ok = !h1_codec_tx_add_header(hreq->base.lib_rs,
- "Connection", "close");
- }
- }
- }
-
- qd_parse_free(app_props);
- qd_iterator_free(app_props_iter);
- }
- }
- free(status_str);
- qd_iterator_free(subject_iter);
- }
- return ok;
-}
-
-
-static uint64_t _encode_response_message(_client_request_t *hreq,
- _client_response_msg_t *rmsg)
-{
- qdr_http1_connection_t *hconn = hreq->base.hconn;
- qd_message_t *msg = qdr_delivery_message(rmsg->dlv);
-
- if (!rmsg->headers_encoded) {
- rmsg->headers_encoded = true;
- if (!_encode_response_headers(hreq, rmsg)) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] message headers malformed - discarding.",
- hconn->conn_id, hconn->out_link_id);
- return PN_REJECTED;
- }
- }
-
- qd_message_stream_data_t *stream_data = 0;
-
- while (true) {
- switch (qd_message_next_stream_data(msg, &stream_data)) {
-
- case QD_MESSAGE_STREAM_DATA_BODY_OK:
-
- qd_log(hconn->adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Encoding response body data",
- hconn->conn_id, hconn->out_link_id);
-
- if (h1_codec_tx_body(hreq->base.lib_rs, stream_data)) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] body data encode failed",
- hconn->conn_id, hconn->out_link_id);
- return PN_REJECTED;
- }
- break;
-
- case QD_MESSAGE_STREAM_DATA_FOOTER_OK:
- // ignore footers
- qd_message_stream_data_release(stream_data);
- break;
-
- case QD_MESSAGE_STREAM_DATA_NO_MORE:
- // indicate this message is complete
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] response message encoding completed",
- hconn->conn_id, hconn->out_link_id);
- return PN_ACCEPTED;
-
- case QD_MESSAGE_STREAM_DATA_INCOMPLETE:
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] body data need more",
- hconn->conn_id, hconn->out_link_id);
- return 0; // wait for more
-
- case QD_MESSAGE_STREAM_DATA_INVALID:
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] Rejecting corrupted body data.",
- hconn->conn_id, hconn->out_link_id);
- return PN_REJECTED;
- }
- }
-}
-
-
-// The I/O thread wants to send this delivery containing the response out the
-// link. It is unlikely that the parsing of this message will fail since the
-// message was constructed by the ingress router. However if the message fails
-// to parse then there is probably no recovering as the client will now be out
-// of sync. For now close the connection if an error occurs.
-//
-uint64_t qdr_http1_client_core_link_deliver(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_link_t *link,
- qdr_delivery_t *delivery,
- bool settled)
-{
- qd_message_t *msg = qdr_delivery_message(delivery);
-
- _client_request_t *hreq = (_client_request_t*) qdr_delivery_get_context(delivery);
- if (!hreq) {
- // new delivery - look for corresponding request via correlation_id
- switch (qd_message_check_depth(msg, QD_DEPTH_PROPERTIES)) {
- case QD_MESSAGE_DEPTH_INCOMPLETE:
- return 0;
-
- case QD_MESSAGE_DEPTH_INVALID:
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] Malformed HTTP/1.x message",
- hconn->conn_id, link->identity);
- qd_message_set_send_complete(msg);
- qdr_http1_close_connection(hconn, "Malformed response message");
- return PN_REJECTED;
-
- case QD_MESSAGE_DEPTH_OK:
- hreq = _lookup_request_context(hconn, msg);
- if (!hreq) {
- // No corresponding request found
- // @TODO(kgiusti) how to handle this? - simply discard?
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] Discarding malformed message.", hconn->conn_id, link->identity);
- qd_message_set_send_complete(msg);
- qdr_http1_close_connection(hconn, "Cannot correlate response message");
- return PN_REJECTED;
- }
-
- // link request state and delivery
- _client_response_msg_t *rmsg = new__client_response_msg_t();
- ZERO(rmsg);
- rmsg->dlv = delivery;
- DEQ_INIT(rmsg->out_data);
- qdr_delivery_set_context(delivery, hreq);
- qdr_delivery_incref(delivery, "HTTP1 client referencing response delivery");
- DEQ_INSERT_TAIL(hreq->responses, rmsg);
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP received response for msg-id=%"PRIu64,
- hconn->conn_id, hconn->out_link_id, hreq->base.msg_id);
- break;
- }
- }
-
- // deliveries arrive one at a time and are added to the tail
- _client_response_msg_t *rmsg = DEQ_TAIL(hreq->responses);
- assert(rmsg && rmsg->dlv == delivery);
-
- // when aggregating responses, they are saved on the list until
- // the request has been settled, then encoded in the configured
- // aggregation format
- if (hconn->cfg.aggregation != QD_AGGREGATION_NONE) {
- if (!qd_message_receive_complete(msg)) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"][L%"PRIu64"] Response incomplete (%zu responses received)", hconn->conn_id, link->identity, DEQ_SIZE(hreq->responses));
- return 0;
- }
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"][L%"PRIu64"] Received response (%zu responses received), settling", hconn->conn_id, link->identity, DEQ_SIZE(hreq->responses));
- rmsg->dispo = PN_ACCEPTED;
- qd_message_set_send_complete(msg);
- qdr_link_flow(qdr_http1_adaptor->core, link, 1, false);
- qdr_delivery_remote_state_updated(qdr_http1_adaptor->core,
- rmsg->dlv,
- rmsg->dispo,
- true, // settled,
- 0, // delivery state
- false);
- return PN_ACCEPTED;
- }
-
- if (!rmsg->dispo) {
- rmsg->dispo = _encode_response_message(hreq, rmsg);
- if (rmsg->dispo) {
- qd_message_set_send_complete(msg);
- if (rmsg->dispo == PN_ACCEPTED) {
- bool need_close = false;
- h1_codec_tx_done(hreq->base.lib_rs, &need_close);
- hreq->close_on_complete = need_close || hreq->close_on_complete;
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] HTTP response message msg-id=%"PRIu64" encoding complete",
- hconn->conn_id, link->identity, hreq->base.msg_id);
-
- } else {
- // The response was bad. This should not happen since the
- // response was created by the remote HTTP/1.x adaptor. Likely
- // a bug? There's not much that can be done to recover, so for
- // now just drop the connection to the client. Note that
- // returning a terminal disposition will cause the delivery to
- // be updated and settled.
- qdr_http1_close_connection(hconn, "Cannot parse response message");
- return rmsg->dispo;
- }
- }
- }
-
- return 0;
-}
-
-
-//
-// Misc
-//
-
-
-// return true if value appears in token list. The compare
-// is case-insensitive.
-//
-static bool _find_token(const char *list, const char *value)
-{
- size_t len;
- const size_t token_len = strlen(value);
- const char *token = h1_codec_token_list_next(list, &len, &list);
- while (token) {
- if (len == token_len && strncasecmp(token, value, token_len) == 0) {
- return true;
- }
- token = h1_codec_token_list_next(list, &len, &list);
- }
- return false;
-}
-
-
-// free the response message
-//
-static void _client_response_msg_free(_client_request_t *req, _client_response_msg_t *rmsg)
-{
- DEQ_REMOVE(req->responses, rmsg);
- qdr_http1_out_data_cleanup(&rmsg->out_data);
-
- if (rmsg->dlv) {
- assert(DEQ_IS_EMPTY(rmsg->out_data)); // expect no held references to message data
- qdr_delivery_set_context(rmsg->dlv, 0);
- qdr_delivery_decref(qdr_http1_adaptor->core, rmsg->dlv, "HTTP1 client response delivery settled");
- rmsg->dlv = 0;
- }
-
- free__client_response_msg_t(rmsg);
-}
-
-
-// Check the head response message for buffers that need to be sent
-//
-static void _write_pending_response(_client_request_t *hreq)
-{
- if (hreq && !hreq->cancelled) {
- assert(DEQ_PREV(&hreq->base) == 0); // must preserve order
- _client_response_msg_t *rmsg = DEQ_HEAD(hreq->responses);
- if (rmsg && DEQ_HEAD(rmsg->out_data)) {
- uint64_t written = qdr_http1_write_out_data(hreq->base.hconn, &rmsg->out_data);
- hreq->base.out_http1_octets += written;
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"] %"PRIu64" octets written",
- hreq->base.hconn->conn_id, written);
- }
- }
-}
-
-
-static void _client_request_free(_client_request_t *hreq)
-{
- if (hreq) {
- // deactivate the Q2 callback
- qd_message_t *msg = hreq->request_dlv ? qdr_delivery_message(hreq->request_dlv) : hreq->request_msg;
- qd_message_clear_q2_unblocked_handler(msg);
-
- qdr_http1_request_base_cleanup(&hreq->base);
- qd_message_free(hreq->request_msg);
- if (hreq->request_dlv) {
- qdr_delivery_set_context(hreq->request_dlv, 0);
- qdr_delivery_decref(qdr_http1_adaptor->core, hreq->request_dlv, "HTTP1 client request delivery settled");
- }
- qd_compose_free(hreq->request_props);
-
- _client_response_msg_t *rmsg = DEQ_HEAD(hreq->responses);
- while (rmsg) {
- _client_response_msg_free(hreq, rmsg);
- rmsg = DEQ_HEAD(hreq->responses);
- }
-
- free__client_request_t(hreq);
- }
-}
-
-
-// release client-specific state
-void qdr_http1_client_conn_cleanup(qdr_http1_connection_t *hconn)
-{
- for (_client_request_t *hreq = (_client_request_t*) DEQ_HEAD(hconn->requests);
- hreq;
- hreq = (_client_request_t*) DEQ_HEAD(hconn->requests)) {
- _client_request_free(hreq);
- }
-}
-
-
-// handle connection close request from management
-//
-void qdr_http1_client_core_conn_close(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn)
-{
- // initiate close of the raw conn. the adaptor will call
- // qdr_connection_close() and clean up once the DISCONNECT
- // event is processed
- //
- qdr_http1_close_connection(hconn, 0);
-}
-
-static void _deliver_request(qdr_http1_connection_t *hconn, _client_request_t *hreq)
-{
- if (hconn->cfg.event_channel) {
- qd_iterator_t *addr = qd_message_field_iterator(hreq->request_msg, QD_FIELD_TO);
- qd_iterator_reset_view(addr, ITER_VIEW_ADDRESS_HASH);
- hreq->request_dlv = qdr_link_deliver_to(hconn->in_link, hreq->request_msg, 0, addr, false, 0, 0, 0, 0);
- } else {
- hreq->request_dlv = qdr_link_deliver(hconn->in_link, hreq->request_msg, 0, false, 0, 0, 0, 0);
- }
- qdr_delivery_set_context(hreq->request_dlv, (void*) hreq);
- hreq->request_msg = 0;
-}
diff --git a/src/adaptors/http1/http1_codec.c b/src/adaptors/http1/http1_codec.c
deleted file mode 100644
index 2acd561..0000000
--- a/src/adaptors/http1/http1_codec.c
+++ /dev/null
@@ -1,1769 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include "qpid/dispatch/http1_codec.h"
-
-#include "qpid/dispatch/alloc_pool.h"
-#include "qpid/dispatch/buffer.h"
-#include "qpid/dispatch/discriminator.h"
-#include "qpid/dispatch/iterator.h"
-
-#include <assert.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
-
-//
-// This file contains code for encoding/decoding an HTTP/1.x data stream. See
-// http1_codec.h for details.
-//
-
-
-// @TODO(kgiusti)
-// - properly set 'more' flag on rx_body() callback
-
-
-const uint8_t CR_TOKEN = '\r';
-const uint8_t LF_TOKEN = '\n';
-const char *CRLF = "\r\n";
-const char *DOUBLE_HYPHEN = "--";
-const char *CONTENT_TYPE_KEY = "Content-Type";
-const char *MULTIPART_CONTENT_TYPE_PREFIX = "multipart/mixed; boundary=";
-const qd_iterator_pointer_t NULL_I_PTR = {0};
-
-// true for informational response codes
-#define IS_INFO_RESPONSE(code) ((code) / 100 == 1)
-
-// true if response code indicates that the response will NOT contain a body
-// 204 = No Content
-// 205 = Reset Content
-// 304 = Not Modified
-#define NO_BODY_RESPONSE(code) \
- ((code) == 204 || \
- (code) == 205 || \
- (code) == 304 || \
- IS_INFO_RESPONSE(code))
-
-
-typedef enum {
- HTTP1_MSG_STATE_START = 0, // parsing start-line
- HTTP1_MSG_STATE_HEADERS, // parsing headers
- HTTP1_MSG_STATE_BODY, // parsing body
- HTTP1_MSG_STATE_DONE, // parsing complete
-} http1_msg_state_t;
-
-
-typedef enum {
- HTTP1_CHUNK_HEADER = 0, // waiting for chunk header
- HTTP1_CHUNK_DATA, // reading chunk data
- HTTP1_CHUNK_TRAILERS, // reading until lone CRLF
-} http1_chunk_state_t;
-
-
-typedef struct scratch_memory_t {
- uint8_t *buf;
- size_t size; // of allocated memory, not contents!
-} scratch_memory_t;
-
-
-// State for a single request-response transaction.
-//
-// A new state is created when a request starts (either via the rx_request
-// callback in the case of client connections or the h1_codec_tx_request() call
-// for server connections).
-//
-// For a connection to a server the rx_response callbacks will occur in the same
-// order as h1_codec_tx_request calls are made.
-//
-// For a connection to a client the caller must ensure that calls to
-// h1_codec_tx_response() must be made in the same order as rx_request callbacks
-// occur.
-//
-struct h1_codec_request_state_t {
- DEQ_LINKS(struct h1_codec_request_state_t);
- void *context;
- h1_codec_connection_t *conn;
- char *method;
- char *target;
- uint32_t response_code;
-
- uint64_t in_octets; // # encoded octets arriving from endpoint
- uint64_t out_octets; // # encoded octets written to endpoint
-
- bool no_body_method; // true if request method is either HEAD or CONNECT
- bool request_complete; // true when request message done encoding/decoding
- bool response_complete; // true when response message done encoding/decoding
- bool close_expected; // if true do not signal request_complete cb until closed
-};
-DEQ_DECLARE(h1_codec_request_state_t, h1_codec_request_state_list_t);
-ALLOC_DECLARE(h1_codec_request_state_t);
-ALLOC_DEFINE(h1_codec_request_state_t);
-
-
-// The HTTP/1.1 connection
-//
-struct h1_codec_connection_t {
- void *context;
-
- // http requests are added to tail,
- // in-progress response is at head
- h1_codec_request_state_list_t hrs_queue;
-
- // Decoder for current incoming msg.
- //
- // incoming: holds the raw data received by the proactor from this
- // connection.
- //
- // read_ptr: points to the next octet to be decoded on the incoming buffer
- // list. Remaining is the length of the raw data to be decoded.
- //
- // body_ptr: points to the first unconsumed octet of the message
- // body. Remaining is the number of octets that may be consumed.
- // Invariant: body_ptr.buffer always points to the incoming.head as body
- // data is being parsed.
- //
- struct decoder_t {
- qd_buffer_list_t incoming;
- qd_iterator_pointer_t read_ptr;
- qd_iterator_pointer_t body_ptr;
-
- h1_codec_request_state_t *hrs; // current request/response
- http1_msg_state_t state;
- scratch_memory_t scratch;
- const char *error_msg;
- int error;
-
- intmax_t content_length;
- http1_chunk_state_t chunk_state;
- uint64_t chunk_length;
-
- bool is_request;
- bool is_chunked;
- bool is_http10;
-
- // decoded headers
- bool hdr_transfer_encoding;
- bool hdr_content_length;
- bool hdr_conn_close; // Connection: close
- bool hdr_conn_keep_alive; // Connection: keep-alive
- } decoder;
-
- // Encoder for current outgoing msg.
- // outgoing: holds the encoded data that needs to be sent to proactor for
- // sending out this connection
- // write_ptr: points to the first empty octet to be written to by the
- // encoder. Remaining is the total unused space in the outgoing list
- // (capacity)
- // Note that the outgoing list and the write_ptr are only used for the
- // start line and headers. Body content buffer chains are past directly to
- // the connection without encoding.
- //
- struct encoder_t {
- qd_buffer_list_t outgoing;
- qd_iterator_pointer_t write_ptr;
- h1_codec_request_state_t *hrs; // current request/response state
-
- bool headers_sent; // true after all headers have been sent
- bool is_request;
- bool is_chunked;
-
- char *boundary_marker;//used for multipart content
-
- // headers provided
- bool hdr_content_length;
- } encoder;
-
- h1_codec_config_t config;
-};
-ALLOC_DECLARE(h1_codec_connection_t);
-ALLOC_DEFINE(h1_codec_connection_t);
-
-static void decoder_reset(struct decoder_t *d);
-static void encoder_reset(struct encoder_t *e);
-
-
-// Create a new request state - this is done when a new http request occurs
-// Keep oldest outstanding tranfer at DEQ_HEAD(conn->hrs_queue)
-static h1_codec_request_state_t *h1_codec_request_state(h1_codec_connection_t *conn)
-{
- h1_codec_request_state_t *hrs = new_h1_codec_request_state_t();
- ZERO(hrs);
- hrs->conn = conn;
- DEQ_INSERT_TAIL(conn->hrs_queue, hrs);
- return hrs;
-}
-
-
-static void h1_codec_request_state_free(h1_codec_request_state_t *hrs)
-{
- if (hrs) {
- h1_codec_connection_t *conn = hrs->conn;
- assert(conn->decoder.hrs != hrs);
- assert(conn->encoder.hrs != hrs);
- DEQ_REMOVE(conn->hrs_queue, hrs);
- free(hrs->method);
- free(hrs->target);
- free_h1_codec_request_state_t(hrs);
- }
-}
-
-
-h1_codec_connection_t *h1_codec_connection(h1_codec_config_t *config, void *context)
-{
- h1_codec_connection_t *conn = new_h1_codec_connection_t();
- ZERO(conn);
-
- conn->context = context;
- conn->config = *config;
- DEQ_INIT(conn->hrs_queue);
-
- encoder_reset(&conn->encoder);
- DEQ_INIT(conn->encoder.outgoing);
- conn->encoder.write_ptr = NULL_I_PTR;
-
- decoder_reset(&conn->decoder);
- DEQ_INIT(conn->decoder.incoming);
- conn->decoder.read_ptr = NULL_I_PTR;
-
- return conn;
-}
-
-
-// Free the connection
-//
-void h1_codec_connection_free(h1_codec_connection_t *conn)
-{
- if (conn) {
- // expect application to cancel all requests!
- assert(DEQ_IS_EMPTY(conn->hrs_queue));
- decoder_reset(&conn->decoder);
- encoder_reset(&conn->encoder);
- qd_buffer_list_free_buffers(&conn->decoder.incoming);
- qd_buffer_list_free_buffers(&conn->encoder.outgoing);
- free(conn->decoder.scratch.buf);
-
- free_h1_codec_connection_t(conn);
- }
-}
-
-
-// reset the rx decoder state after message received
-//
-static void decoder_reset(struct decoder_t *decoder)
-{
- // do not touch the read_ptr or incoming buffer list as they
- // track the current position in the incoming data stream
-
- decoder->body_ptr = NULL_I_PTR;
- decoder->hrs = 0;
- decoder->state = HTTP1_MSG_STATE_START;
- decoder->content_length = 0;
- decoder->chunk_state = HTTP1_CHUNK_HEADER;
- decoder->chunk_length = 0;
- decoder->error = 0;
- decoder->error_msg = 0;
- decoder->is_request = false;
- decoder->is_chunked = false;
- decoder->is_http10 = false;
- decoder->hdr_transfer_encoding = false;
- decoder->hdr_content_length = false;
- decoder->hdr_conn_close = false;
- decoder->hdr_conn_keep_alive = false;
-}
-
-
-// reset the tx encoder after message sent
-static void encoder_reset(struct encoder_t *encoder)
-{
- // do not touch the write_ptr or the outgoing queue as there may be more messages to send.
- encoder->hrs = 0;
- encoder->headers_sent = false;
- encoder->is_request = false;
- encoder->is_chunked = false;
- encoder->hdr_content_length = false;
- if (encoder->boundary_marker) {
- free(encoder->boundary_marker);
- encoder->boundary_marker = 0;
- }
-}
-
-
-// convert a string representation of a Content-Length value to
-// and integer. Return true if parse succeeds
-//
-static bool _parse_content_length(const char *clen, intmax_t *value)
-{
- // a valid value is an integer >= 0
- *value = 0;
- return sscanf(clen, "%"PRIdMAX, value) == 1 && *value > -1;
-}
-
-
-// Scan the value of a Transfer-Encoding header to see if the
-// last encoding is chunked
-//
-static bool _is_transfer_chunked(const char *encoding)
-{
- // "chunked" must be the last item in the value string. And remember kids:
- // coding type names are case insensitive!
- //
- size_t len = strlen(encoding);
- if (len >= 7) { // 7 = strlen("chunked")
- const char *ptr = encoding + len - 7;
- return strcasecmp("chunked", ptr) == 0;
- }
- return false;
-}
-
-
-// ensure the encoder has at least capacity octets available
-//
-static void ensure_outgoing_capacity(struct encoder_t *encoder, size_t capacity)
-{
- while (encoder->write_ptr.remaining < capacity) {
- qd_buffer_t *buf = qd_buffer();
- DEQ_INSERT_TAIL(encoder->outgoing, buf);
- encoder->write_ptr.remaining += qd_buffer_capacity(buf);
- }
- if (!encoder->write_ptr.buffer) {
- encoder->write_ptr.buffer = DEQ_HEAD(encoder->outgoing);
- encoder->write_ptr.cursor = qd_buffer_cursor(encoder->write_ptr.buffer);
- }
-}
-
-
-// Write a C string to the encoder.
-//
-static void write_string(struct encoder_t *encoder, const char *string)
-{
- size_t needed = strlen(string);
- if (needed == 0) return;
-
- ensure_outgoing_capacity(encoder, needed);
-
- encoder->hrs->out_octets += needed;
- qd_iterator_pointer_t *wptr = &encoder->write_ptr;
- while (needed) {
- if (qd_buffer_capacity(wptr->buffer) == 0) {
- wptr->buffer = DEQ_NEXT(wptr->buffer);
- wptr->cursor = qd_buffer_base(wptr->buffer);
- }
-
- size_t avail = MIN(needed, qd_buffer_capacity(wptr->buffer));
- memcpy(wptr->cursor, string, avail);
- qd_buffer_insert(wptr->buffer, avail);
- wptr->cursor += avail;
- wptr->remaining -= avail;
- string += avail;
- needed -= avail;
- }
-}
-
-
-//
-static inline size_t skip_octets(qd_iterator_pointer_t *data, size_t amount)
-{
- size_t count = 0;
- amount = MIN(data->remaining, amount);
- while (count < amount) {
- if (data->cursor == qd_buffer_cursor(data->buffer)) {
- data->buffer = DEQ_NEXT(data->buffer);
- assert(data->buffer); // else data->remaining is bad
- data->cursor = qd_buffer_base(data->buffer);
- }
- size_t available = qd_buffer_cursor(data->buffer) - data->cursor;
- available = MIN(available, amount - count);
- data->cursor += available;
- count += available;
- }
- data->remaining -= amount;
- return amount;
-}
-
-// consume next octet and advance the pointer
-static inline bool get_octet(qd_iterator_pointer_t *data, uint8_t *octet)
-{
- if (data->remaining > 0) {
- if (data->cursor == qd_buffer_cursor(data->buffer)) {
- data->buffer = DEQ_NEXT(data->buffer);
- data->cursor = qd_buffer_base(data->buffer);
- }
- *octet = *data->cursor;
- data->cursor += 1;
- data->remaining -= 1;
- return true;
- }
- return false;
-}
-
-
-// True if line contains just "CRLF"
-//
-static bool is_empty_line(const qd_iterator_pointer_t *line)
-{
- if (line->remaining == 2) {
- qd_iterator_pointer_t tmp = *line;
- uint8_t octet;
- return (get_octet(&tmp, &octet) && octet == CR_TOKEN
- && get_octet(&tmp, &octet) && octet == LF_TOKEN);
- }
- return false;
-}
-
-
-// for debug:
-static void debug_print_iterator_pointer(const char *prefix, const qd_iterator_pointer_t *ptr)
-{
-#if 0
- qd_iterator_pointer_t tmp = *ptr;
- fprintf(stdout, "%s '", prefix);
- size_t len = MIN(tmp.remaining, 80);
- uint8_t octet;
- while (len-- > 0 && get_octet(&tmp, &octet)) {
- fputc(octet, stdout);
- }
- fprintf(stdout, "%s'\n", (tmp.remaining) ? " <truncated>" : "");
- fflush(stdout);
-#endif
-}
-
-
-// read a CRLF terminated line starting at 'data'.
-// On success, 'data' is advanced to the octet following the LF and 'line' is
-// set to the read line (including trailing CRLF). Returns false if no CRLF found
-//
-static bool read_line(qd_iterator_pointer_t *data, qd_iterator_pointer_t *line)
-{
- qd_iterator_pointer_t tmp = *data;
-
- *line = *data;
- line->remaining = 0;
-
- bool eol = false;
-
- uint8_t octet;
- while (!eol && get_octet(&tmp, &octet)) {
- line->remaining += 1;
- if (octet == CR_TOKEN) {
- if (get_octet(&tmp, &octet)) {
- line->remaining += 1;
- if (octet == LF_TOKEN) {
- eol = true;
- }
- }
- }
- }
-
- if (eol) {
- *data = tmp;
- return true;
- } else {
- *line = NULL_I_PTR;
- return false;
- }
-}
-
-
-static bool ensure_scratch_size(scratch_memory_t *b, size_t required)
-{
- if (b->size < required) {
- if (b->buf)
- free(b->buf);
- b->size = required;
- b->buf = malloc(b->size);
- }
-
- // @TODO(kgiusti): deal with malloc failure
- return true;
-}
-
-
-// return true if octet in str
-static inline bool filter_str(const char *str, uint8_t octet)
-{
- const char *ptr = strchr(str, (int)((unsigned int)octet));
- return ptr && *ptr != 0;
-}
-
-
-// trims any optional whitespace characters at the start of 'line'
-// RFC7230 defines OWS as zero or more spaces or horizontal tabs
-//
-static void trim_whitespace(qd_iterator_pointer_t *line)
-{
- qd_iterator_pointer_t ptr = *line;
- size_t skip = 0;
- uint8_t octet;
- while (get_octet(&ptr, &octet) && isblank(octet))
- skip += 1;
- if (skip)
- skip_octets(line, skip);
-}
-
-// copy out iterator to a buffer and null terminate. Return # of bytes written
-// to str including terminating null.
-static size_t pointer_2_str(const qd_iterator_pointer_t *line, unsigned char *str, size_t len)
-{
- assert(len);
- qd_iterator_pointer_t tmp = *line;
- uint8_t *ptr = (uint8_t *)str;
- len -= 1; // reserve for null terminator
- while (len-- > 0 && get_octet(&tmp, ptr))
- ++ptr;
- *ptr++ = 0;
- return ptr - (uint8_t *)str;
-}
-
-
-// Parse out a token as defined by RFC7230 and store the result in 'token'.
-// 'line' is advanced past the token. This is used for parsing fields that
-// RFC7230 defines as 'tokens'.
-//
-static bool parse_token(qd_iterator_pointer_t *line, qd_iterator_pointer_t *token)
-{
- static const char *TOKEN_EXTRA = "!#$%&’*+-.^_‘|~";
-
- trim_whitespace(line);
- qd_iterator_pointer_t tmp = *line;
- *token = tmp;
- size_t len = 0;
- uint8_t octet;
- while (get_octet(&tmp, &octet)
- && (('A' <= octet && octet <= 'Z') ||
- ('a' <= octet && octet <= 'z') ||
- ('0' <= octet && octet <= '9') ||
- (filter_str(TOKEN_EXTRA, octet)))) {
- len++;
- }
-
- if (len) {
- token->remaining = len;
- skip_octets(line, len);
- return true;
- }
- *token = NULL_I_PTR;
- return false;
-}
-
-
-// Parse out a text field delineated by whitespace.
-// 'line' is advanced past the field.
-//
-static bool parse_field(qd_iterator_pointer_t *line, qd_iterator_pointer_t *field)
-{
- trim_whitespace(line);
- qd_iterator_pointer_t tmp = *line;
- *field = tmp;
- size_t len = 0;
- uint8_t octet;
- while (get_octet(&tmp, &octet) && !isspace(octet))
- len++;
-
- if (len) {
- field->remaining = len;
- skip_octets(line, len);
- return true;
- }
- *field = NULL_I_PTR;
- return false;
-}
-
-
-// parse the HTTP/1.1 request line:
-// "method SP request-target SP HTTP-version CRLF"
-//
-static bool parse_request_line(h1_codec_connection_t *conn, struct decoder_t *decoder, qd_iterator_pointer_t *line)
-{
- qd_iterator_pointer_t method = {0};
- qd_iterator_pointer_t target = {0};
- qd_iterator_pointer_t version = {0};
- int in_octets = line->remaining;
-
- if (!parse_token(line, &method) ||
- !parse_field(line, &target) ||
- !parse_field(line, &version)) {
-
- decoder->error_msg = "Malformed request line";
- decoder->error = HTTP1_STATUS_BAD_REQ;
- return decoder->error;
- }
-
- // translate iterator pointers to C strings
- ensure_scratch_size(&decoder->scratch, method.remaining + target.remaining + version.remaining + 3);
- uint8_t *ptr = decoder->scratch.buf;
- size_t avail = decoder->scratch.size;
-
- uint8_t *method_str = ptr;
- size_t offset = pointer_2_str(&method, method_str, avail);
- ptr += offset;
- avail -= offset;
-
- uint8_t *target_str = ptr;
- offset = pointer_2_str(&target, target_str, avail);
- ptr += offset;
- avail += offset;
-
- uint8_t *version_str = ptr;
- pointer_2_str(&version, version_str, avail);
-
- uint32_t major = 0;
- uint32_t minor = 0;
- if (sscanf((char*)version_str, "HTTP/%"SCNu32".%"SCNu32, &major, &minor) != 2) {
- decoder->error_msg = "Malformed version in request";
- decoder->error = HTTP1_STATUS_BAD_REQ;
- return decoder->error;
- }
-
- if (major != 1 || minor > 1) {
- decoder->error_msg = "Unsupported HTTP version";
- decoder->error = HTTP1_STATUS_BAD_VERSION;
- return decoder->error;
- }
-
- decoder->is_http10 = minor == 0;
-
- h1_codec_request_state_t *hrs = h1_codec_request_state(conn);
-
- // check for methods that do not support body content in the response:
- hrs->no_body_method = (strcmp((char*)method_str, "HEAD") == 0 ||
- strcmp((char*)method_str, "CONNECT") == 0);
-
- hrs->method = qd_strdup((char*) method_str);
- hrs->target = qd_strdup((char*) target_str);
- hrs->in_octets += in_octets;
-
- decoder->hrs = hrs;
- decoder->is_request = true;
-
- decoder->error = conn->config.rx_request(hrs, (char*)method_str, (char*)target_str, major, minor);
- if (decoder->error)
- decoder->error_msg = "hrs_rx_request callback error";
- return decoder->error;
-}
-
-
-// parse the HTTP/1.1 response line
-// "HTTP-version SP status-code [SP reason-phrase] CRLF"
-//
-static int parse_response_line(h1_codec_connection_t *conn, struct decoder_t *decoder, qd_iterator_pointer_t *line)
-{
- qd_iterator_pointer_t version = {0};
- qd_iterator_pointer_t status_code = {0};
- qd_iterator_pointer_t reason = {0};
- int in_octets = line->remaining;
-
- if (!parse_field(line, &version)
- || !parse_field(line, &status_code)
- || status_code.remaining != 3) {
-
- decoder->error_msg = "Malformed response status line";
- decoder->error = HTTP1_STATUS_SERVER_ERR;
- return decoder->error;
- }
-
- // Responses arrive in the same order as requests are generated so this new
- // response corresponds to head hrs
- h1_codec_request_state_t *hrs = DEQ_HEAD(conn->hrs_queue);
- if (!hrs) {
- // receiving a response without a corresponding request
- decoder->error_msg = "Spurious HTTP response received";
- decoder->error = HTTP1_STATUS_SERVER_ERR;
- return decoder->error;
- }
-
- assert(!decoder->hrs); // state machine violation
- assert(hrs->response_code == 0);
-
- hrs->in_octets += in_octets;
- decoder->hrs = hrs;
-
- unsigned char code_str[4];
- pointer_2_str(&status_code, code_str, 4);
- hrs->response_code = atoi((char*) code_str);
-
- // the reason phrase is optional, and may contain spaces
-
- reason = *line;
- if (reason.remaining >= 2) // expected for CRLF
- reason.remaining -= 2;
- trim_whitespace(&reason);
-
- // convert to C strings
- ensure_scratch_size(&decoder->scratch, version.remaining + reason.remaining + 2);
- uint8_t *ptr = decoder->scratch.buf;
- size_t avail = decoder->scratch.size;
-
- uint8_t *version_str = ptr;
- size_t offset = pointer_2_str(&version, version_str, avail);
- ptr += offset;
- avail -= offset;
-
- uint8_t *reason_str = ptr;
- offset = pointer_2_str(&reason, reason_str, avail);
-
- uint32_t major = 0;
- uint32_t minor = 0;
- if (sscanf((char*)version_str, "HTTP/%"SCNu32".%"SCNu32, &major, &minor) != 2) {
- decoder->error_msg = "Malformed version in response";
- decoder->error = HTTP1_STATUS_SERVER_ERR;
- return decoder->error;
- }
-
- if (major != 1 || minor > 1) {
- decoder->error_msg = "Unsupported HTTP version";
- decoder->error = HTTP1_STATUS_BAD_VERSION;
- return decoder->error;
- }
-
- decoder->is_request = false;
- decoder->is_http10 = minor == 0;
-
- decoder->error = conn->config.rx_response(decoder->hrs,
- hrs->response_code,
- (offset) ? (char*)reason_str: 0,
- major, minor);
- if (decoder->error)
- decoder->error_msg = "hrs_rx_response callback error";
-
- return decoder->error;
-}
-
-
-// parse the first line of an incoming http message
-//
-static bool parse_start_line(h1_codec_connection_t *conn, struct decoder_t *decoder)
-{
- qd_iterator_pointer_t *rptr = &decoder->read_ptr;
- qd_iterator_pointer_t line;
-
- if (read_line(rptr, &line)) {
- debug_print_iterator_pointer("start line:", &line);
-
- if (!is_empty_line(&line)) { // RFC7230: ignore any preceding CRLF
- if (conn->config.type == HTTP1_CONN_CLIENT) {
- parse_request_line(conn, decoder, &line);
- } else {
- parse_response_line(conn, decoder, &line);
- }
- conn->decoder.state = HTTP1_MSG_STATE_HEADERS;
- }
- return !!rptr->remaining;
- }
-
- return false; // pend for more input
-}
-
-//
-// Header parsing
-//
-
-// Called after the last incoming header was decoded and passed to the
-// application
-//
-static bool process_headers_done(h1_codec_connection_t *conn, struct decoder_t *decoder)
-{
- // Flush all buffers processed so far - no longer needed
-
- qd_buffer_t *head = DEQ_HEAD(decoder->incoming);
- while (head && head != decoder->read_ptr.buffer) {
- DEQ_REMOVE_HEAD(decoder->incoming);
- qd_buffer_free(head);
- head = DEQ_HEAD(decoder->incoming);
- }
-
- // perform any post-headers validation:
-
- if (decoder->is_request) {
- if (decoder->hdr_transfer_encoding && !decoder->is_chunked) {
- // RFC7230 Message Body Length: If a Transfer-Encoding header field
- // is present in a request and the chunked transfer coding is not
- // the final encoding, the message body length cannot be determined
- // reliably; the server MUST respond with the 400 (Bad Request)
- // status code and then close the connection.
- decoder->error_msg = "Non-chunked Tranfer-Encoding in request";
- decoder->error = HTTP1_STATUS_BAD_REQ;
- return false;
- }
- }
-
- // determine if a body is present (ref RFC7230 sec 3.3.3 Message Body Length)
- bool has_body;
- if (decoder->is_request) {
- // an http request will have a body ONLY if either chunked transfer or
- // non-zero Content-Length header was given.
- has_body = (decoder->is_chunked || decoder->content_length);
-
- } else {
- // An HTTP response has a body if request method is NOT HEAD or CONNECT AND
- // the response code indicates a body. A body will either have a specific
- // size via Content-Length or chunked encoder, OR its length is unspecified
- // and the message body is terminated by closing the connection.
- //
- h1_codec_request_state_t *hrs = decoder->hrs;
- has_body = !(hrs->no_body_method || NO_BODY_RESPONSE(hrs->response_code));
- if (has_body) {
- // no body if explicit Content-Length of zero
- if (decoder->hdr_content_length && decoder->content_length == 0) {
- has_body = false;
- }
- }
-
- // In certain scenarios an HTTP server will close the connection to
- // indicate the end of a response message. This may happen even if
- // the request message has a known length (Content-Length or
- // Transfer-Encoding). In these circumstances do NOT signal that
- // the request is complete (call request_complete() callback) until
- // the connection closes. Otherwise the user may start sending the
- // next request message before the HTTP server closes the TCP
- // connection. (see RFC7230, section Persistence)
- hrs->close_expected = decoder->hdr_conn_close
- || (decoder->is_http10 && !decoder->hdr_conn_keep_alive);
- }
-
- decoder->error = conn->config.rx_headers_done(decoder->hrs, has_body);
- if (decoder->error) {
- decoder->error_msg = "hrs_rx_headers_done callback error";
- return false;
- }
-
- if (has_body) {
- // start tracking the body buffer chain
- decoder->body_ptr = decoder->read_ptr;
- decoder->body_ptr.remaining = 0;
- decoder->state = HTTP1_MSG_STATE_BODY;
- } else {
- decoder->state = HTTP1_MSG_STATE_DONE;
- }
-
- return !!decoder->read_ptr.remaining;
-}
-
-
-// process a received header to determine message body length, etc.
-//
-static int process_header(h1_codec_connection_t *conn, struct decoder_t *decoder, const uint8_t *key, const uint8_t *value)
-{
- int parse_error = decoder->is_request ? HTTP1_STATUS_BAD_REQ : HTTP1_STATUS_SERVER_ERR;
-
- if (strcasecmp("Content-Length", (char*) key) == 0) {
- intmax_t old = decoder->content_length;
- if (!_parse_content_length((char*) value, &decoder->content_length)) {
- decoder->error_msg = "Malformed Content-Length header";
- decoder->error = parse_error;
- return decoder->error;
- }
- if (old && old != decoder->content_length) {
- decoder->error_msg = "Invalid duplicate Content-Length header";
- decoder->error = parse_error;
- return decoder->error;
- }
- decoder->hdr_content_length = true;
-
- } else if (strcasecmp("Transfer-Encoding", (char*) key) == 0) {
- decoder->is_chunked = _is_transfer_chunked((char*) value);
- decoder->hdr_transfer_encoding = true;
-
- } else if (strcasecmp("Connection", (char*) key) == 0) {
- // parse out connection lifecycle options
- const char *token = 0;
- size_t len = 0;
- const char *next = (const char*) value;
- while (*next && (token = h1_codec_token_list_next(next, &len, &next)) != 0) {
- if (len == 5 && strncmp("close", token, 5) == 0) {
- decoder->hdr_conn_close = true;
- } else if (len == 10 && strncmp("keep-alive", token, 10) == 0) {
- decoder->hdr_conn_keep_alive = true;
- }
- }
- }
-
- return 0;
-}
-
-
-// Parse an HTTP header line.
-// See RFC7230 for details. If header line folding (obs-folding) is detected,
-// replace the folding with spaces.
-//
-static bool parse_header(h1_codec_connection_t *conn, struct decoder_t *decoder)
-{
- qd_iterator_pointer_t end_ptr = decoder->read_ptr;
- h1_codec_request_state_t *hrs = decoder->hrs;
- qd_iterator_pointer_t line;
-
- assert(hrs); // else state machine busted
-
- if (!read_line(&end_ptr, &line))
- // need more data
- return false;
-
- if (is_empty_line(&line)) {
- decoder->read_ptr = end_ptr;
- hrs->in_octets += line.remaining;
- return process_headers_done(conn, decoder);
- }
-
- // check for header line folding
-
- bool obs_fold = false;
- while (true) {
- qd_iterator_pointer_t peek = end_ptr;
- uint8_t octet;
- if (!get_octet(&peek, &octet))
- // need more data
- return false;
-
- if (octet != ' ' && octet != '\t')
- break;
-
- obs_fold = true;
-
- if (!read_line(&end_ptr, &line))
- return false;
- }
-
- // end_ptr now points past the header line, advance decoder past header
- // line and set 'line' to hold header
-
- line = decoder->read_ptr;
- decoder->read_ptr = end_ptr;
- line.remaining -= end_ptr.remaining;
-
- debug_print_iterator_pointer("header:", &line);
-
- hrs->in_octets += line.remaining;
-
- // convert field to key and value strings
-
- qd_iterator_pointer_t key;
- if (!parse_token(&line, &key)) {
- decoder->error_msg = "Malformed Header";
- decoder->error = (decoder->is_request) ? HTTP1_STATUS_BAD_REQ
- : HTTP1_STATUS_SERVER_ERR;
- return false;
- }
-
- // advance line past the ':'
- uint8_t octet;
- while (get_octet(&line, &octet) && octet != ':')
- ;
-
- // line now contains the value. convert to C strings and post callback
- ensure_scratch_size(&decoder->scratch, key.remaining + line.remaining + 2);
- uint8_t *ptr = decoder->scratch.buf;
- size_t avail = decoder->scratch.size;
-
- uint8_t *key_str = ptr;
- size_t offset = pointer_2_str(&key, key_str, avail);
- ptr += offset;
- avail -= offset;
-
- uint8_t *value_str = ptr;
- pointer_2_str(&line, value_str, avail);
-
- // trim whitespace on both ends of value
- while (isspace(*value_str))
- ++value_str;
- ptr = value_str + strlen((char*) value_str);
- while (ptr-- > value_str) {
- if (!isspace(*ptr))
- break;
- *ptr = 0;
- }
-
- // remove header line folding by overwriting all <CR> and <LF> chars with
- // spaces as per RFC7230
-
- if (obs_fold) {
- ptr = value_str;
- while ((ptr = (uint8_t*) strpbrk((char*) ptr, CRLF)) != 0)
- *ptr = ' ';
- }
-
- process_header(conn, decoder, key_str, value_str);
-
- if (!decoder->error) {
- decoder->error = conn->config.rx_header(hrs, (char *)key_str, (char *)value_str);
- if (decoder->error)
- decoder->error_msg = "hrs_rx_header callback error";
- }
-
- return !!decoder->read_ptr.remaining;
-}
-
-
-//
-// Chunked body encoding parser
-//
-
-
-// Pass message body data up to the application.
-//
-static inline int consume_stream_data(h1_codec_connection_t *conn, bool flush)
-{
- struct decoder_t *decoder = &conn->decoder;
- qd_iterator_pointer_t *body_ptr = &decoder->body_ptr;
- qd_iterator_pointer_t *rptr = &decoder->read_ptr;
- qd_buffer_list_t blist = DEQ_EMPTY;
- size_t octets = 0;
-
- // invariant:
- assert(DEQ_HEAD(decoder->incoming) == body_ptr->buffer);
-
- // The read pointer points to somewhere in the buffer chain that contains some
- // unparsed data. Send any buffers preceding the current read pointer.
- while (body_ptr->remaining) {
-
- if (body_ptr->buffer == rptr->buffer && rptr->remaining > 0)
- break;
-
- DEQ_REMOVE_HEAD(decoder->incoming);
-
- size_t offset = body_ptr->cursor - qd_buffer_base(body_ptr->buffer);
- if (offset) {
- // most (all?) message buffer operations assume the message
- // data starts at the buffer_base. Adjust accordingly
- memmove(qd_buffer_base(body_ptr->buffer),
- qd_buffer_base(body_ptr->buffer) + offset,
- qd_buffer_size(body_ptr->buffer) - offset);
- body_ptr->cursor = qd_buffer_base(body_ptr->buffer);
- body_ptr->buffer->size -= offset;
- }
-
- if (qd_buffer_size(body_ptr->buffer) > 0) {
- DEQ_INSERT_TAIL(blist, body_ptr->buffer);
- octets += qd_buffer_size(body_ptr->buffer);
- body_ptr->remaining -= qd_buffer_size(body_ptr->buffer);
- } else {
- qd_buffer_free(body_ptr->buffer);
- }
- body_ptr->buffer = DEQ_HEAD(decoder->incoming);
- body_ptr->cursor = body_ptr->buffer ? qd_buffer_base(body_ptr->buffer) : 0;
- }
-
- // invariant:
- assert(body_ptr->remaining >= 0);
-
- // At this point if there is any body bytes remaining they are in the same
- // buffer as the unparsed input (rptr).
-
- if (flush && body_ptr->remaining) {
- // need to copy out remaining body octets into new buffer
- qd_buffer_t *tail = qd_buffer();
-
- assert(body_ptr->remaining <= qd_buffer_capacity(tail));
- memcpy(qd_buffer_cursor(tail), body_ptr->cursor, body_ptr->remaining);
- qd_buffer_insert(tail, body_ptr->remaining);
- DEQ_INSERT_TAIL(blist, tail);
- octets += body_ptr->remaining;
- *body_ptr = *rptr;
- body_ptr->remaining = 0;
- }
-
- if (octets) {
- decoder->hrs->in_octets += octets;
- decoder->error = conn->config.rx_body(decoder->hrs, &blist, octets, true);
- }
- return decoder->error;
-}
-
-
-
-// parsing the start of a chunked header:
-// <chunk size in hex>CRLF
-//
-static bool parse_body_chunked_header(h1_codec_connection_t *conn, struct decoder_t *decoder)
-{
- qd_iterator_pointer_t *rptr = &decoder->read_ptr;
- qd_iterator_pointer_t line;
-
- assert(decoder->chunk_state == HTTP1_CHUNK_HEADER);
- assert(decoder->chunk_length == 0);
-
- if (read_line(rptr, &line)) {
- decoder->body_ptr.remaining += line.remaining;
-
- ensure_scratch_size(&decoder->scratch, line.remaining + 1);
- uint8_t *ptr = decoder->scratch.buf;
- pointer_2_str(&line, (unsigned char*) ptr, line.remaining + 1);
- int rc = sscanf((char*) ptr, "%"SCNx64, &decoder->chunk_length);
- if (rc != 1) {
- decoder->error_msg = "Invalid chunk header";
- decoder->error = (decoder->is_request) ? HTTP1_STATUS_BAD_REQ
- : HTTP1_STATUS_SERVER_ERR;
- return false;
- }
-
- if (decoder->chunk_length == 0) {
- // last chunk
- decoder->chunk_state = HTTP1_CHUNK_TRAILERS;
- } else {
- decoder->chunk_state = HTTP1_CHUNK_DATA;
-
- // chunk_length does not include the CRLF trailer:
- decoder->chunk_length += 2;
- }
-
-
- return !!rptr->remaining;
- }
-
- return false; // pend for more input
-}
-
-
-// Parse the data section of a chunk
-//
-static bool parse_body_chunked_data(h1_codec_connection_t *conn, struct decoder_t *decoder)
-{
- qd_iterator_pointer_t *rptr = &decoder->read_ptr;
- qd_iterator_pointer_t *body_ptr = &decoder->body_ptr;
-
- assert(decoder->chunk_state == HTTP1_CHUNK_DATA);
-
- size_t skipped = skip_octets(rptr, decoder->chunk_length);
- decoder->chunk_length -= skipped;
- body_ptr->remaining += skipped;
-
- consume_stream_data(conn, false);
-
- if (decoder->chunk_length == 0) {
- // end of chunk
- decoder->chunk_state = HTTP1_CHUNK_HEADER;
- }
-
- return !!rptr->remaining;
-}
-
-
-// Keep reading chunk trailers until the terminating empty line is read
-//
-static bool parse_body_chunked_trailer(h1_codec_connection_t *conn, struct decoder_t *decoder)
-{
- qd_iterator_pointer_t *rptr = &decoder->read_ptr;
- qd_iterator_pointer_t *body_ptr = &decoder->body_ptr;
- qd_iterator_pointer_t line;
-
- assert(decoder->chunk_state == HTTP1_CHUNK_TRAILERS);
-
- if (read_line(rptr, &line)) {
- body_ptr->remaining += line.remaining;
- if (is_empty_line(&line)) {
- // end of message
- consume_stream_data(conn, true);
- decoder->state = HTTP1_MSG_STATE_DONE;
- }
-
- return !!rptr->remaining;
- }
-
- return false; // pend for full line
-}
-
-
-// parse an incoming message body which is chunk encoded
-// Return True if there is more data pending to parse
-static bool parse_body_chunked(h1_codec_connection_t *conn, struct decoder_t *decoder)
-{
- bool more = true;
- switch (decoder->chunk_state) {
-
- case HTTP1_CHUNK_HEADER:
- more = parse_body_chunked_header(conn, decoder);
- break;
-
- case HTTP1_CHUNK_DATA:
- more = parse_body_chunked_data(conn, decoder);
- break;
-
- case HTTP1_CHUNK_TRAILERS:
- more = parse_body_chunked_trailer(conn, decoder);
- break;
- } // end switch
-
- return more;
-}
-
-
-// parse an incoming message body which is Content-Length bytes long
-//
-static bool parse_body_content(h1_codec_connection_t *conn, struct decoder_t *decoder)
-{
- qd_iterator_pointer_t *rptr = &decoder->read_ptr;
- qd_iterator_pointer_t *body_ptr = &decoder->body_ptr;
-
- size_t skipped = skip_octets(rptr, decoder->content_length);
- decoder->content_length -= skipped;
- body_ptr->remaining += skipped;
- bool eom = decoder->content_length == 0;
-
- consume_stream_data(conn, eom);
- if (eom)
- decoder->state = HTTP1_MSG_STATE_DONE;
-
- return !!rptr->remaining;
-}
-
-
-static bool parse_body(h1_codec_connection_t *conn, struct decoder_t *decoder)
-{
- if (decoder->is_chunked)
- return parse_body_chunked(conn, decoder);
-
- if (decoder->content_length)
- return parse_body_content(conn, decoder);
-
- // otherwise no explict body size, so just keep passing the entire unparsed
- // incoming chain along until the remote closes the connection
- if (decoder->read_ptr.remaining) {
- // extend body_ptr to consume all unparsed read data
- decoder->body_ptr.remaining += decoder->read_ptr.remaining;
- decoder->read_ptr.remaining = 0;
- decoder->read_ptr.buffer = DEQ_TAIL(decoder->incoming);
- decoder->read_ptr.cursor = qd_buffer_cursor(decoder->read_ptr.buffer);
- consume_stream_data(conn, true);
- decoder->body_ptr = decoder->read_ptr = NULL_I_PTR;
- DEQ_INIT(decoder->incoming);
- }
-
- return false;
-}
-
-
-// Called when incoming message is complete
-//
-static bool parse_done(h1_codec_connection_t *conn, struct decoder_t *decoder)
-{
- h1_codec_request_state_t *hrs = decoder->hrs;
- bool is_response = !decoder->is_request;
-
- // signal the message receive is complete
- conn->config.rx_done(hrs);
-
- if (is_response) {
- // Informational 1xx response codes are NOT terminal - further responses are allowed!
- if (IS_INFO_RESPONSE(hrs->response_code)) {
- hrs->response_code = 0;
- } else {
- hrs->response_complete = true;
- }
- } else {
- hrs->request_complete = true;
- }
-
- decoder_reset(decoder);
-
- if (!hrs->close_expected) {
- if (hrs->request_complete && hrs->response_complete) {
- conn->config.request_complete(hrs, false);
- h1_codec_request_state_free(hrs);
- }
- return !!decoder->read_ptr.remaining;
- } else
- return false; // stop parsing input, wait for close
-}
-
-
-// Main decode loop.
-// Process received data until it is exhausted
-//
-static int decode_incoming(h1_codec_connection_t *conn)
-{
- struct decoder_t *decoder = &conn->decoder;
- bool more = true;
- while (more && !decoder->error) {
-
- if (decoder->state == HTTP1_MSG_STATE_START)
- more = parse_start_line(conn, decoder);
-
- else if (decoder->state == HTTP1_MSG_STATE_HEADERS)
- more = parse_header(conn, decoder);
-
- else if (decoder->state == HTTP1_MSG_STATE_BODY)
- more = parse_body(conn, decoder);
-
- // Can reach DONE from any call above.
- if (decoder->state == HTTP1_MSG_STATE_DONE)
- more = parse_done(conn, decoder);
- }
-
- return decoder->error;
-}
-
-
-void *h1_codec_connection_get_context(h1_codec_connection_t *conn)
-{
- return conn->context;
-}
-
-// Push inbound network data into the http1 protocol engine.
-//
-// All of the hrs_rx callback will occur in the context of this call. This
-// returns zero on success otherwise an error code. Any error occuring during
-// a callback will be reflected in the return value of this function.
-//
-int h1_codec_connection_rx_data(h1_codec_connection_t *conn, qd_buffer_list_t *data, size_t len)
-{
- struct decoder_t *decoder = &conn->decoder;
- bool init_ptrs = DEQ_IS_EMPTY(decoder->incoming);
-
- DEQ_APPEND(decoder->incoming, *data);
-
- if (init_ptrs) {
- decoder->read_ptr.buffer = DEQ_HEAD(decoder->incoming);
- decoder->read_ptr.cursor = qd_buffer_base(decoder->read_ptr.buffer);
- decoder->read_ptr.remaining = len;
-
- if (decoder->state == HTTP1_MSG_STATE_BODY) {
- decoder->body_ptr = decoder->read_ptr;
- decoder->body_ptr.remaining = 0;
- }
- } else {
- decoder->read_ptr.remaining += len;
- }
-
- return decode_incoming(conn);
-}
-
-
-// The read channel of the connection has closed. If this is a connection to a
-// server this may simply be the end of the response message. If a message is
-// currently being decoded see if it is valid to complete.
-//
-void h1_codec_connection_rx_closed(h1_codec_connection_t *conn)
-{
- if (conn && conn->config.type == HTTP1_CONN_SERVER) {
-
- // terminate the in progress decode
-
- struct decoder_t *decoder = &conn->decoder;
- h1_codec_request_state_t *hrs = decoder->hrs;
- if (hrs) {
- // consider the response complete if length is unspecified since in
- // this case the server must close the connection to complete the
- // message body. However if the response message is a "continue"
- // then the final response never arrived and the response is
- // incomplete
- if (decoder->state == HTTP1_MSG_STATE_BODY
- && !IS_INFO_RESPONSE(hrs->response_code)
- && !decoder->is_chunked
- && !decoder->hdr_content_length) {
-
- if (!hrs->response_complete) {
- hrs->response_complete = true;
- conn->config.rx_done(hrs);
- }
- }
- }
-
- decoder_reset(decoder);
- // since the underlying connection is gone discard all remaining
- // incoming data
- qd_buffer_list_free_buffers(&conn->decoder.incoming);
- decoder->read_ptr = NULL_I_PTR;
-
- // check if current request is completed
- hrs = DEQ_HEAD(conn->hrs_queue);
- if (hrs) {
- hrs->close_expected = false; // the close just occurred
- if (hrs->response_complete && hrs->request_complete) {
- conn->config.request_complete(hrs, false);
- h1_codec_request_state_free(hrs);
- }
- }
- }
-}
-
-
-void h1_codec_request_state_set_context(h1_codec_request_state_t *hrs, void *context)
-{
- hrs->context = context;
-}
-
-
-void *h1_codec_request_state_get_context(const h1_codec_request_state_t *hrs)
-{
- return hrs->context;
-}
-
-
-h1_codec_connection_t *h1_codec_request_state_get_connection(const h1_codec_request_state_t *hrs)
-{
- return hrs->conn;
-}
-
-
-const char *h1_codec_request_state_method(const h1_codec_request_state_t *hrs)
-{
- return hrs->method;
-}
-
-const char *h1_codec_request_state_target(const h1_codec_request_state_t *hrs)
-{
- return hrs->target;
-}
-
-const uint32_t h1_codec_request_state_response_code(const h1_codec_request_state_t *hrs)
-{
- return hrs->response_code;
-}
-
-
-void h1_codec_request_state_cancel(h1_codec_request_state_t *hrs)
-{
- if (hrs) {
- h1_codec_connection_t *conn = hrs->conn;
- if (hrs == conn->decoder.hrs) {
- decoder_reset(&conn->decoder);
- }
- if (hrs == conn->encoder.hrs) {
- encoder_reset(&conn->encoder);
- }
- conn->config.request_complete(hrs, true);
- h1_codec_request_state_free(hrs);
- }
-}
-
-
-// initiate a new HTTP request. This creates a new request state.
-// request = <method>SP<target>SP<version>CRLF
-//
-h1_codec_request_state_t *h1_codec_tx_request(h1_codec_connection_t *conn, const char *method, const char *target,
- uint32_t version_major, uint32_t version_minor)
-{
- struct encoder_t *encoder = &conn->encoder;
- assert(!encoder->hrs); // error: transfer already in progress
- assert(conn->config.type == HTTP1_CONN_SERVER);
-
- h1_codec_request_state_t *hrs = encoder->hrs = h1_codec_request_state(conn);
- encoder->is_request = true;
- encoder->headers_sent = false;
-
- hrs->method = qd_strdup(method);
- hrs->target = qd_strdup(target);
-
- // check for methods that do not support body content in the response:
- hrs->no_body_method = (strcmp((char*)method, "HEAD") == 0 ||
- strcmp((char*)method, "CONNECT") == 0);
-
- write_string(encoder, method);
- write_string(encoder, " ");
- write_string(encoder, target);
- write_string(encoder, " ");
- {
- char version[64];
- snprintf(version, 64, "HTTP/%"PRIu32".%"PRIu32, version_major, version_minor);
- write_string(encoder, version);
- }
- write_string(encoder, CRLF);
-
- return hrs;
-}
-
-
-// Send an HTTP response msg. hrs must correspond to the "oldest" outstanding
-// request that arrived via the hrs_rx_request callback for this connection.
-// status_code is expected to be 100 <= status_code <= 999
-// status-line = HTTP-version SP status-code SP reason-phrase CRLF
-//
-int h1_codec_tx_response(h1_codec_request_state_t *hrs, int status_code, const char *reason_phrase,
- uint32_t version_major, uint32_t version_minor)
-{
- h1_codec_connection_t *conn = h1_codec_request_state_get_connection(hrs);
- struct encoder_t *encoder = &conn->encoder;
-
- assert(conn->config.type == HTTP1_CONN_CLIENT);
- assert(!encoder->hrs); // error: transfer already in progress
- assert(DEQ_HEAD(conn->hrs_queue) == hrs); // error: response not in order!
- assert(hrs->response_code == 0);
-
- encoder->hrs = hrs;
- encoder->is_request = false;
- encoder->headers_sent = false;
- hrs->response_code = status_code;
-
- {
- char version[64];
- snprintf(version, 64, "HTTP/%"PRIu32".%"PRIu32, version_major, version_minor);
- write_string(encoder, version);
- }
-
- write_string(encoder, " ");
-
- {
- char code_str[32];
- snprintf(code_str, 32, "%d", status_code);
- write_string(encoder, code_str);
- }
-
- if (reason_phrase) {
- write_string(encoder, " ");
- write_string(encoder, reason_phrase);
- }
- write_string(encoder, CRLF);
-
- return 0;
-}
-
-
-// Add a header field to an outgoing message
-// header-field = field-name ":" OWS field-value OWS
-int h1_codec_tx_add_header(h1_codec_request_state_t *hrs, const char *key, const char *value)
-{
- h1_codec_connection_t *conn = h1_codec_request_state_get_connection(hrs);
- struct encoder_t *encoder = &conn->encoder;
- assert(encoder->hrs == hrs); // hrs not current transfer
-
- write_string(encoder, key);
- write_string(encoder, ": ");
- write_string(encoder, value);
- write_string(encoder, CRLF);
-
- // determine if the body length is provided. If not
- // the caller will have to close the connection
- //
- if (strcasecmp("Content-Length", (char*) key) == 0) {
- encoder->hdr_content_length = true;
- } else if (strcasecmp("Transfer-Encoding", (char *)key) == 0) {
- encoder->is_chunked = _is_transfer_chunked(value);
- }
-
- // check to see if there are any full buffers that can be sent.
-
- qd_buffer_list_t blist = DEQ_EMPTY;
- qd_buffer_t *buf = DEQ_HEAD(encoder->outgoing);
- size_t octets = 0;
- while (buf && buf != encoder->write_ptr.buffer) {
- DEQ_REMOVE_HEAD(encoder->outgoing);
- DEQ_INSERT_TAIL(blist, buf);
- octets += qd_buffer_size(buf);
- buf = DEQ_HEAD(encoder->outgoing);
- }
- if (!DEQ_IS_EMPTY(blist))
- conn->config.tx_buffers(hrs, &blist, octets);
-
- return 0;
-}
-
-
-static inline void _flush_output(h1_codec_request_state_t *hrs, struct encoder_t *encoder)
-{
- // flush all pending output. From this point out the outgoing queue is
- // no longer used for this message
- hrs->conn->config.tx_buffers(hrs, &encoder->outgoing, qd_buffer_list_length(&encoder->outgoing));
- DEQ_INIT(encoder->outgoing);
- encoder->write_ptr = NULL_I_PTR;
-}
-
-static inline void _flush_headers(h1_codec_request_state_t *hrs, struct encoder_t *encoder)
-{
- if (!encoder->headers_sent) {
- // need to terminate any headers by sending the plain CRLF that follows
- // the headers
- write_string(encoder, CRLF);
- _flush_output(hrs, encoder);
- encoder->headers_sent = true;
- }
-}
-
-int h1_codec_tx_begin_multipart(h1_codec_request_state_t *hrs)
-{
- h1_codec_connection_t *conn = h1_codec_request_state_get_connection(hrs);
- struct encoder_t *encoder = &conn->encoder;
- encoder->boundary_marker = (char*) malloc(QD_DISCRIMINATOR_SIZE + 2);
- qd_generate_discriminator(encoder->boundary_marker);
- char *content_type = (char*) malloc(strlen(MULTIPART_CONTENT_TYPE_PREFIX) + strlen(encoder->boundary_marker) + 1);
- strcpy(content_type, MULTIPART_CONTENT_TYPE_PREFIX);
- strcpy(content_type + strlen(content_type), encoder->boundary_marker);
- h1_codec_tx_add_header(hrs, CONTENT_TYPE_KEY, content_type);
- free(content_type);
-
- return 0;
-}
-
-int h1_codec_tx_begin_multipart_section(h1_codec_request_state_t *hrs)
-{
- h1_codec_connection_t *conn = h1_codec_request_state_get_connection(hrs);
- struct encoder_t *encoder = &conn->encoder;
-
- //reset headers_sent flag for the new section
- encoder->headers_sent = false;
- write_string(encoder, CRLF);
- write_string(encoder, DOUBLE_HYPHEN);
- write_string(encoder, encoder->boundary_marker);
- write_string(encoder, CRLF);
-
- return 0;
-}
-
-int h1_codec_tx_end_multipart(h1_codec_request_state_t *hrs)
-{
- h1_codec_connection_t *conn = h1_codec_request_state_get_connection(hrs);
- struct encoder_t *encoder = &conn->encoder;
-
- write_string(encoder, CRLF);
- write_string(encoder, DOUBLE_HYPHEN);
- write_string(encoder, encoder->boundary_marker);
- write_string(encoder, DOUBLE_HYPHEN);
- write_string(encoder, CRLF);
- encoder->headers_sent = false;
- _flush_headers(hrs, encoder);
-
- free(encoder->boundary_marker);
- encoder->boundary_marker = 0;
-
- return 0;
-}
-
-
-uint64_t h1_codec_tx_multipart_section_boundary_length()
-{
- return QD_DISCRIMINATOR_SIZE + 4 + 2;
-}
-
-uint64_t h1_codec_tx_multipart_end_boundary_length()
-{
- return QD_DISCRIMINATOR_SIZE + 4 + 4;
-}
-
-// just forward the body chain along
-int h1_codec_tx_body(h1_codec_request_state_t *hrs, qd_message_stream_data_t *stream_data)
-{
- h1_codec_connection_t *conn = h1_codec_request_state_get_connection(hrs);
- struct encoder_t *encoder = &conn->encoder;
-
- if (!encoder->headers_sent)
- _flush_headers(hrs, encoder);
-
- // skip the outgoing queue and send directly
- hrs->out_octets += qd_message_stream_data_payload_length(stream_data);
- conn->config.tx_stream_data(hrs, stream_data);
-
- return 0;
-}
-
-int h1_codec_tx_body_str(h1_codec_request_state_t *hrs, char *data)
-{
- h1_codec_connection_t *conn = h1_codec_request_state_get_connection(hrs);
- struct encoder_t *encoder = &conn->encoder;
- if (!encoder->headers_sent) {
- // need to terminate any headers by sending the plain CRLF that follows
- // the headers
- write_string(encoder, CRLF);
- encoder->headers_sent = true;
- }
- write_string(encoder, data);
- _flush_output(hrs, encoder);
- return 0;
-}
-
-int h1_codec_tx_done(h1_codec_request_state_t *hrs, bool *need_close)
-{
- h1_codec_connection_t *conn = h1_codec_request_state_get_connection(hrs);
- struct encoder_t *encoder = &conn->encoder;
- if (need_close)
- *need_close = false;
-
- if (!encoder->headers_sent)
- _flush_headers(hrs, encoder);
-
- bool is_response = !encoder->is_request;
-
- if (is_response) {
- if (IS_INFO_RESPONSE(hrs->response_code)) {
- // this is a non-terminal response. Another response is expected
- // for this request so just reset the transfer state
- hrs->response_code = 0;
- } else {
- hrs->response_complete = true;
- if (need_close) {
- // if the message body size is not explicit the connection has
- // to be closed to indicate end of message
- if (!hrs->no_body_method &&
- !NO_BODY_RESPONSE(hrs->response_code) &&
- !encoder->is_chunked &&
- !encoder->hdr_content_length) {
-
- *need_close = true;
- }
- }
- }
- } else {
- hrs->request_complete = true;
- }
-
- encoder_reset(encoder);
-
- if (!hrs->close_expected) {
- if (hrs->request_complete && hrs->response_complete) {
- conn->config.request_complete(hrs, false);
- h1_codec_request_state_free(hrs);
- }
- }
-
- return 0;
-}
-
-
-bool h1_codec_request_complete(const h1_codec_request_state_t *hrs)
-{
- return hrs && hrs->request_complete;
-}
-
-
-bool h1_codec_response_complete(const h1_codec_request_state_t *hrs)
-{
- return hrs && hrs->response_complete;
-}
-
-
-void h1_codec_request_state_counters(const h1_codec_request_state_t *hrs,
- uint64_t *in_octets, uint64_t *out_octets)
-{
- *in_octets = (hrs) ? hrs->in_octets : 0;
- *out_octets = (hrs) ? hrs->out_octets : 0;
-}
-
-
-const char *h1_codec_token_list_next(const char *start, size_t *len, const char **next)
-{
- static const char *SKIPME = ", \t";
-
- *len = 0;
- *next = 0;
-
- if (!start) return 0;
-
- while (*start && filter_str(SKIPME, *start))
- ++start;
-
- if (!*start) return 0;
-
- const char *end = start;
- while (*end && !filter_str(SKIPME, *end))
- ++end;
-
- *len = end - start;
- while (*end && filter_str(SKIPME, *end))
- ++end;
-
- *next = end;
- return start;
-}
diff --git a/src/adaptors/http1/http1_private.h b/src/adaptors/http1/http1_private.h
deleted file mode 100644
index 39bbd1b..0000000
--- a/src/adaptors/http1/http1_private.h
+++ /dev/null
@@ -1,271 +0,0 @@
-#ifndef http1_private_H
-#define http1_private_H 1
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-//
-// HTTP/1.x Adaptor Internals
-//
-// Nomenclature:
-// "in": for information flowing from the endpoint into the router
-// (from proactor to core)
-// "out": for information flowing from the router out to the endpoint
-// (from core to proactor)
-//
-#include "adaptors/http_common.h"
-
-#include "qpid/dispatch/http1_codec.h"
-#include "qpid/dispatch/message.h"
-#include "qpid/dispatch/protocol_adaptor.h"
-
-// for debug: will dump I/O buffer content to stdout if true
-#define HTTP1_DUMP_BUFFERS false
-
-#define HTTP1_IO_BUF_SIZE 16384
-
-typedef struct qdr_http1_out_data_t qdr_http1_out_data_t;
-typedef struct qdr_http1_request_base_t qdr_http1_request_base_t;
-typedef struct qdr_http1_connection_t qdr_http1_connection_t;
-
-DEQ_DECLARE(qdr_http1_connection_t, qdr_http1_connection_list_t);
-
-typedef struct qdr_http1_adaptor_t {
- qdr_core_t *core;
- qdr_protocol_adaptor_t *adaptor;
- qd_log_source_t *log;
- sys_mutex_t *lock; // for the lists and activation
- qd_http_listener_list_t listeners;
- qd_http_connector_list_t connectors;
- qdr_http1_connection_list_t connections;
-} qdr_http1_adaptor_t;
-
-extern qdr_http1_adaptor_t *qdr_http1_adaptor;
-
-
-// Data to be written out the raw connection.
-//
-// This adaptor has to cope with two different data sources: the HTTP1 encoder
-// and the qd_message_stream_data_t list. The HTTP1 encoder produces a simple
-// qd_buffer_list_t for outgoing header data whose ownership is given to the
-// adaptor: the adaptor is free to dequeue/free these buffers as needed. The
-// qd_message_stream_data_t buffers are shared with the owning message and the
-// buffer list must not be modified by the adaptor. The qdr_http1_out_data_t
-// is used to manage both types of data sources.
-//
-struct qdr_http1_out_data_t {
- DEQ_LINKS(qdr_http1_out_data_t);
-
- // data is either in a raw buffer chain
- // or a message body data (not both!)
-
- qd_buffer_list_t raw_buffers;
- qd_message_stream_data_t *stream_data;
-
- // points to the data contained in the stream_data/raw_buffers
- qd_iterator_t *data_iter;
-};
-ALLOC_DECLARE(qdr_http1_out_data_t);
-DEQ_DECLARE(qdr_http1_out_data_t, qdr_http1_out_data_list_t);
-
-
-// Per HTTP request/response(s) state.
-//
-// This base class is extended for client and server-specific state, see
-// http1_client.c and http1_server.c A reference is stored in
-// qdr_delivery_get_context(dlv)
-//
-struct qdr_http1_request_base_t {
- DEQ_LINKS(qdr_http1_request_base_t);
-
- uint64_t msg_id;
- h1_codec_request_state_t *lib_rs;
- qdr_http1_connection_t *hconn; // parent connection
- char *response_addr; // request reply-to
- char *site;
- qd_timestamp_t start;
- qd_timestamp_t stop;
- uint64_t out_http1_octets;
-};
-DEQ_DECLARE(qdr_http1_request_base_t, qdr_http1_request_list_t);
-
-
-// A single HTTP adaptor connection.
-//
-struct qdr_http1_connection_t {
- DEQ_LINKS(qdr_http1_connection_t);
- qd_server_t *qd_server;
- h1_codec_connection_t *http_conn;
- pn_raw_connection_t *raw_conn;
- qdr_connection_t *qdr_conn;
- qdr_http1_adaptor_t *adaptor;
-
- uint64_t conn_id;
- qd_handler_context_t handler_context;
- h1_codec_connection_type_t type;
- qd_conn_admin_status_t admin_status;
- qd_conn_oper_status_t oper_status;
-
- struct {
- char *host;
- char *port;
- char *address;
- char *site;
- char *host_port;
- bool event_channel;
- qd_http_aggregation_t aggregation;
- char *host_override;
- } cfg;
-
- // State if connected to an HTTP client
- //
- struct {
- char *client_ip_addr;
- char *reply_to_addr; // set once link is up
- uint64_t next_msg_id;
- } client;
-
- // State if connected to an HTTP server
- struct {
- qd_http_connector_t *connector;
- qd_timer_t *reconnect_timer;
- qd_timestamp_t link_timeout;
- qd_duration_t reconnect_pause;
- } server;
-
- // Outgoing link (router ==> HTTP app)
- //
- qdr_link_t *out_link;
- uint64_t out_link_id;
- int out_link_credit; // provided by adaptor
-
- // Incoming link (HTTP app ==> router)
- //
- qdr_link_t *in_link;
- uint64_t in_link_id;
- int in_link_credit; // provided by router
- sys_atomic_t q2_restart; // signal to resume receive
- bool q2_blocked; // stop reading from raw conn
-
- // Oldest at HEAD
- //
- qdr_http1_request_list_t requests;
-
- // statistics
- //
- uint64_t in_http1_octets;
- uint64_t out_http1_octets;
-
- // flags
- //
- bool trace;
-
- //
- //
- bool read_buf_busy;
- bool write_buf_busy;
-
- uint8_t read_buffer[HTTP1_IO_BUF_SIZE];
- uint8_t write_buffer[HTTP1_IO_BUF_SIZE];
-};
-ALLOC_DECLARE(qdr_http1_connection_t);
-
-// special AMQP application properties keys for HTTP1 metadata headers
-// ':' prefix is illegal for HTTP headers, ensures no collisions with
-// HTTP headers
-//
-#define VERSION_PROP_KEY ":version" // of msg (string, e.g. "1.1"
-#define PATH_PROP_KEY ":path" // URI target
-#define REASON_PROP_KEY ":reason" // response reason phrase
-
-
-// http1_adaptor.c
-//
-void qdr_http1_free_written_buffers(qdr_http1_connection_t *hconn);
-void qdr_http1_enqueue_buffer_list(qdr_http1_out_data_list_t *fifo, qd_buffer_list_t *blist, uintmax_t octets);
-void qdr_http1_enqueue_stream_data(qdr_http1_out_data_list_t *fifo, qd_message_stream_data_t *stream_data);
-uint64_t qdr_http1_write_out_data(qdr_http1_connection_t *hconn, qdr_http1_out_data_list_t *fifo);
-void qdr_http1_out_data_cleanup(qdr_http1_out_data_list_t *out_data);
-int qdr_http1_grant_read_buffers(qdr_http1_connection_t *hconn);
-uintmax_t qdr_http1_get_read_buffers(qdr_http1_connection_t *hconn,
- qd_buffer_list_t *blist);
-
-void qdr_http1_close_connection(qdr_http1_connection_t *hconn, const char *error);
-void qdr_http1_connection_free(qdr_http1_connection_t *hconn);
-
-void qdr_http1_request_base_cleanup(qdr_http1_request_base_t *hreq);
-void qdr_http1_error_response(qdr_http1_request_base_t *hreq,
- int error_code,
- const char *reason);
-void qdr_http1_rejected_response(qdr_http1_request_base_t *hreq,
- const qdr_error_t *error);
-void qdr_http1_q2_unblocked_handler(const qd_alloc_safe_ptr_t context);
-
-// http1_client.c protocol adaptor callbacks
-//
-void qdr_http1_client_core_link_flow(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_link_t *link,
- int credit);
-uint64_t qdr_http1_client_core_link_deliver(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_link_t *link,
- qdr_delivery_t *delivery,
- bool settled);
-void qdr_http1_client_core_second_attach(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_link_t *link,
- qdr_terminus_t *source,
- qdr_terminus_t *target);
-void qdr_http1_client_core_delivery_update(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_http1_request_base_t *hreqb,
- qdr_delivery_t *dlv,
- uint64_t disp,
- bool settled);
-void qdr_http1_client_conn_cleanup(qdr_http1_connection_t *hconn);
-void qdr_http1_client_core_conn_close(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn);
-
-// http1_server.c protocol adaptor callbacks
-//
-void qdr_http1_server_core_link_flow(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_link_t *link,
- int credit);
-uint64_t qdr_http1_server_core_link_deliver(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_link_t *link,
- qdr_delivery_t *delivery,
- bool settled);
-void qdr_http1_server_core_delivery_update(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_http1_request_base_t *hreq,
- qdr_delivery_t *dlv,
- uint64_t disp,
- bool settled);
-void qdr_http1_server_conn_cleanup(qdr_http1_connection_t *hconn);
-void qdr_http1_server_core_conn_close(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn);
-
-// recording of stats:
-void qdr_http1_record_client_request_info(qdr_http1_adaptor_t *adaptor, qdr_http1_request_base_t *request);
-void qdr_http1_record_server_request_info(qdr_http1_adaptor_t *adaptor, qdr_http1_request_base_t *request);
-
-#endif // http1_private_H
diff --git a/src/adaptors/http1/http1_request_info.c b/src/adaptors/http1/http1_request_info.c
deleted file mode 100644
index ed58be3..0000000
--- a/src/adaptors/http1/http1_request_info.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "http1_private.h"
-
-#include "qpid/dispatch/ctools.h"
-#include "qpid/dispatch/server.h"
-
-#include <inttypes.h>
-
-static void _http1_record_request_info(qdr_http1_adaptor_t *adaptor, qdr_http1_request_base_t *request, const char *host, bool ingress)
-{
- uint64_t in_octets, out_octets;
- h1_codec_request_state_counters(request->lib_rs, &in_octets, &out_octets);
- qd_http_record_request(adaptor->core, h1_codec_request_state_method(request->lib_rs), h1_codec_request_state_response_code(request->lib_rs),
- request->hconn->cfg.address, host, request->hconn->cfg.site, request->site, ingress,
- in_octets, out_octets, request->stop && request->start ? request->stop - request->start : 0);
-}
-
-void qdr_http1_record_client_request_info(qdr_http1_adaptor_t *adaptor, qdr_http1_request_base_t *request)
-{
- char *host = qd_get_host_from_host_port(request->hconn->client.client_ip_addr);
- _http1_record_request_info(adaptor, request, host ? host : request->hconn->client.client_ip_addr, true);
- if (host) free(host);
-}
-
-void qdr_http1_record_server_request_info(qdr_http1_adaptor_t *adaptor, qdr_http1_request_base_t *request)
-{
- _http1_record_request_info(adaptor, request, request->hconn->cfg.host, false);
-}
diff --git a/src/adaptors/http1/http1_server.c b/src/adaptors/http1/http1_server.c
deleted file mode 100644
index a410a18..0000000
--- a/src/adaptors/http1/http1_server.c
+++ /dev/null
@@ -1,1754 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#include "adaptors/adaptor_utils.h"
-#include "http1_private.h"
-
-#include <proton/proactor.h>
-
-//
-// This file contains code specific to HTTP server processing. The raw
-// connection is terminated at an HTTP server, not an HTTP client.
-//
-
-
-//
-// State for a single response message arriving via the raw connection. This
-// message will be decoded into a single AMQP message and forwarded into the
-// core.
-//
-// This object is instantiated when the HTTP1 codec indicates the arrival of a
-// response message (See _server_rx_response_cb()). The response is considered
-// "complete" after it has been fully encoded and delivered to the core. The
-// _server_response_msg_t is freed at this point - we do not wait for dispo or
-// settlement from the core since we cannot do anything meaningful should the
-// delivery fail (other than log it).
-//
-typedef struct _server_response_msg_t {
- DEQ_LINKS(struct _server_response_msg_t);
-
- struct _server_request_t *hreq; // owning request
-
- qd_message_t *msg; // hold incoming message
- qd_composed_field_t *msg_props; // hold incoming headers
- qdr_delivery_t *dlv; // inbound to router (qdr_link_deliver)
- bool rx_complete; // response rx complete
- bool discard; // client no longer present
-} _server_response_msg_t;
-ALLOC_DECLARE(_server_response_msg_t);
-ALLOC_DEFINE(_server_response_msg_t);
-DEQ_DECLARE(_server_response_msg_t, _server_response_msg_list_t);
-
-const char *HOST_KEY = "Host";
-
-//
-// State for an HTTP/1.x Request+Response exchange, server facing
-//
-typedef struct _server_request_t {
- qdr_http1_request_base_t base;
-
- // The request arrives via the router core in an AMQP message
- // (qd_message_t). These fields are used to encode the response and send
- // it out the raw connection.
- //
- qdr_delivery_t *request_dlv; // outbound from core_link_deliver
- uint64_t request_dispo; // set by adaptor during encode
- bool request_settled; // set by adaptor
- bool request_acked; // true if dispo sent to core
- bool request_discard; // drop incoming request data
- bool headers_encoded; // True when header encode done
-
- // fifo of encoded request data to be written out the raw connection.
- // Note well: cannot release request_dlv while there is pending out
- // data since it references body data from the message.
- qdr_http1_out_data_list_t out_data;
-
- _server_response_msg_list_t responses; // response(s) to this request
-
- bool codec_completed; // Request and Response HTTP msgs OK
- bool cancelled;
- bool close_on_complete; // close the conn when this request is complete
- bool response_complete; // true when server response message decoded
-} _server_request_t;
-ALLOC_DECLARE(_server_request_t);
-ALLOC_DEFINE(_server_request_t);
-
-
-//
-// This file contains code specific to HTTP server processing. The raw
-// connection is terminated at an HTTP server, not an HTTP client.
-//
-
-
-#define DEFAULT_CAPACITY 250
-
-// Reconnection logic time values: When the HTTP server disconnects this
-// adaptor will attempt to reconnect. The reconnect interval increases by
-// RETRY_PAUSE_MSEC with each reconnect failure until it hits the maximum of
-// RETRY_MAX_PAUSE_MSEC. If the reconnection does not succeed after
-// LINK_TIMEOUT_MSEC then the qdr_link_t's are detached to prevent client
-// requests from arriving for a potentially dead server.
-#define RETRY_PAUSE_MSEC ((qd_duration_t)500)
-#define RETRY_MAX_PAUSE_MSEC ((qd_duration_t)3000)
-#define LINK_TIMEOUT_MSEC ((qd_duration_t)2500)
-
-static void _server_tx_buffers_cb(h1_codec_request_state_t *lib_hrs, qd_buffer_list_t *blist, unsigned int len);
-static void _server_tx_stream_data_cb(h1_codec_request_state_t *lib_hrs, qd_message_stream_data_t *stream_data);
-static int _server_rx_request_cb(h1_codec_request_state_t *hrs,
- const char *method,
- const char *target,
- uint32_t version_major,
- uint32_t version_minor);
-static int _server_rx_response_cb(h1_codec_request_state_t *hrs,
- int status_code,
- const char *reason_phrase,
- uint32_t version_major,
- uint32_t version_minor);
-static int _server_rx_header_cb(h1_codec_request_state_t *hrs, const char *key, const char *value);
-static int _server_rx_headers_done_cb(h1_codec_request_state_t *hrs, bool has_body);
-static int _server_rx_body_cb(h1_codec_request_state_t *hrs, qd_buffer_list_t *body, size_t len, bool more);
-static void _server_rx_done_cb(h1_codec_request_state_t *hrs);
-static void _server_request_complete_cb(h1_codec_request_state_t *hrs, bool cancelled);
-static void _handle_connection_events(pn_event_t *e, qd_server_t *qd_server, void *context);
-static void _do_reconnect(void *context);
-static void _server_response_msg_free(_server_request_t *req, _server_response_msg_t *rmsg);
-static void _server_request_free(_server_request_t *hreq);
-static void _write_pending_request(_server_request_t *req);
-static void _cancel_request(_server_request_t *req);
-static bool _process_request(_server_request_t *req);
-static void _encode_request_message(_server_request_t *hreq);
-static void _send_request_message(_server_request_t *hreq);
-
-
-////////////////////////////////////////////////////////
-// HTTP/1.x Server Connector
-////////////////////////////////////////////////////////
-
-
-// An HttpConnector has been created. Create an qdr_http_connection_t and a
-// qdr_connection_t for it.
-//
-static qdr_http1_connection_t *_create_server_connection(qd_http_connector_t *ctor,
- qd_dispatch_t *qd,
- const qd_http_bridge_config_t *bconfig)
-{
- qdr_http1_connection_t *hconn = new_qdr_http1_connection_t();
-
- ZERO(hconn);
- hconn->type = HTTP1_CONN_SERVER;
- hconn->admin_status = QD_CONN_ADMIN_ENABLED;
- hconn->oper_status = QD_CONN_OPER_DOWN; // until TCP connection ready
- hconn->qd_server = qd->server;
- hconn->adaptor = qdr_http1_adaptor;
- hconn->handler_context.handler = &_handle_connection_events;
- hconn->handler_context.context = hconn;
- sys_atomic_init(&hconn->q2_restart, 0);
- hconn->cfg.host = qd_strdup(bconfig->host);
- hconn->cfg.port = qd_strdup(bconfig->port);
- hconn->cfg.address = qd_strdup(bconfig->address);
- hconn->cfg.site = bconfig->site ? qd_strdup(bconfig->site) : 0;
- hconn->cfg.host_port = qd_strdup(bconfig->host_port);
- hconn->server.connector = ctor;
- ctor->ctx = (void*)hconn;
- hconn->cfg.event_channel = bconfig->event_channel;
- hconn->cfg.aggregation = bconfig->aggregation;
- hconn->cfg.host_override = bconfig->host_override ? qd_strdup(bconfig->host_override) : 0;
-
- // for initiating a connection to the server
- hconn->server.reconnect_timer = qd_timer(qdr_http1_adaptor->core->qd, _do_reconnect, hconn);
-
- // Create the qdr_connection
- qdr_connection_info_t *info = qdr_connection_info(false, //bool is_encrypted,
- false, //bool is_authenticated,
- true, //bool opened,
- "", //char *sasl_mechanisms,
- QD_OUTGOING, //qd_direction_t dir,
- hconn->cfg.host_port, //const char *host,
- "", //const char *ssl_proto,
- "", //const char *ssl_cipher,
- "", //const char *user,
- "HTTP/1.x Adaptor", //const char *container,
- 0, //pn_data_t *connection_properties,
- 0, //int ssl_ssf,
- false, //bool ssl,
- "", // peer router version,
- false); // streaming links
-
- hconn->conn_id = qd_server_allocate_connection_id(hconn->qd_server);
- hconn->qdr_conn = qdr_connection_opened(qdr_http1_adaptor->core,
- qdr_http1_adaptor->adaptor,
- false, // incoming
- QDR_ROLE_NORMAL,
- 1, // cost
- hconn->conn_id,
- 0, // label
- 0, // remote container id
- false, // strip annotations in
- false, // strip annotations out
- DEFAULT_CAPACITY,
- 0, // vhost
- 0, // policy_spec
- info,
- 0, // bind context
- 0); // bind token
-
- // wait for the raw connection to come up before creating the in and out links
-
- qd_log(hconn->adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"] HTTP connection to server created", hconn->conn_id);
-
- return hconn;
-}
-
-
-// Management Agent API - Create
-//
-// Note that this runs on the Management Agent thread, which may be running concurrently with the
-// I/O and timer threads.
-qd_http_connector_t *qd_http1_configure_connector(qd_dispatch_t *qd, const qd_http_bridge_config_t *config, qd_entity_t *entity)
-{
- qd_http_connector_t *c = qd_http_connector(qd->server);
- if (!c) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_ERROR, "Unable to create http connector: no memory");
- return 0;
- }
- c->config = *config;
- DEQ_ITEM_INIT(c);
-
- qdr_http1_connection_t *hconn = _create_server_connection(c, qd, config);
- if (hconn) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"] Initiating connection to HTTP server %s",
- hconn->conn_id, hconn->cfg.host_port);
-
- // lock out the core activation thread. Up until this point the core
- // thread cannot activate the qdr_connection_t since the
- // qdr_connection_t context has not been set (see
- // _core_connection_activate_CT in http1_adaptor.c). This keeps the
- // core from attempting to schedule the connection until we finish
- // setup.
- sys_mutex_lock(qdr_http1_adaptor->lock);
- DEQ_INSERT_TAIL(qdr_http1_adaptor->connections, hconn);
- DEQ_INSERT_TAIL(qdr_http1_adaptor->connectors, c);
- qdr_connection_set_context(hconn->qdr_conn, hconn);
- qd_timer_schedule(hconn->server.reconnect_timer, 0);
- sys_mutex_unlock(qdr_http1_adaptor->lock);
- // setup complete - core thread can activate the connection
- return c;
- } else {
- qd_http_connector_decref(c);
- c = 0;
- }
-
- return c;
-}
-
-
-// Management Agent API - Delete
-//
-// Note that this runs on the Management Agent thread, which may be running concurrently with the
-// I/O and timer threads.
-void qd_http1_delete_connector(qd_dispatch_t *ignored, qd_http_connector_t *ct)
-{
- if (ct) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_INFO, "Deleted HttpConnector for %s, %s:%s", ct->config.address, ct->config.host, ct->config.port);
-
- sys_mutex_lock(qdr_http1_adaptor->lock);
- DEQ_REMOVE(qdr_http1_adaptor->connectors, ct);
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) ct->ctx;
- qdr_connection_t *qdr_conn = 0;
- if (hconn) {
- hconn->admin_status = QD_CONN_ADMIN_DELETED;
- hconn->server.connector = 0;
- ct->ctx = 0;
- qdr_conn = hconn->qdr_conn;
- }
- sys_mutex_unlock(qdr_http1_adaptor->lock);
-
- if (qdr_conn)
- qdr_core_close_connection(qdr_conn);
- qd_http_connector_decref(ct);
- }
-}
-
-
-
-
-////////////////////////////////////////////////////////
-// Raw Connector Events
-////////////////////////////////////////////////////////
-
-
-// Is the hreq currently in flight to the server?
-//
-static inline bool _is_request_in_progress(const _server_request_t *hreq)
-{
- return hreq && (hreq->base.out_http1_octets > 0 || hreq->cancelled);
-}
-
-
-// Create the qdr links and HTTP codec when the server connection comes up.
-// These links & codec will persist across temporary drops in the connection to
-// the server (like when closing the connection to indicate end of response
-// message). However if the connection to the server cannot be re-established
-// in a "reasonable" amount of time we consider the server unavailable and
-// these links and codec will be closed - aborting any pending requests. Once
-// the connection to the server is reestablished these links & codec will be
-// recreated.
-//
-static void _setup_server_links(qdr_http1_connection_t *hconn)
-{
- if (!hconn->in_link) {
- // simulate an anonymous link for responses from the server
- hconn->in_link = qdr_link_first_attach(hconn->qdr_conn,
- QD_INCOMING,
- qdr_terminus(0), //qdr_terminus_t *source,
- qdr_terminus(0), //qdr_terminus_t *target
- "http1.server.in", //const char *name,
- 0, //const char *terminus_addr,
- false,
- NULL,
- &(hconn->in_link_id));
- qdr_link_set_context(hconn->in_link, hconn);
-
- qd_log(hconn->adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] HTTP server response link created",
- hconn->conn_id, hconn->in_link_id);
- }
-
- if (!hconn->out_link) {
- // simulate a server subscription for its service address
- qdr_terminus_t *source = qdr_terminus(0);
- qdr_terminus_set_address(source, hconn->cfg.address);
- hconn->out_link = qdr_link_first_attach(hconn->qdr_conn,
- QD_OUTGOING,
- source, //qdr_terminus_t *source,
- qdr_terminus(0), //qdr_terminus_t *target,
- "http1.server.out", //const char *name,
- 0, //const char *terminus_addr,
- false,
- 0, // initial delivery
- &(hconn->out_link_id));
- qdr_link_set_context(hconn->out_link, hconn);
-
- hconn->out_link_credit = DEFAULT_CAPACITY;
- qdr_link_flow(hconn->adaptor->core, hconn->out_link, DEFAULT_CAPACITY, false);
-
- qd_log(hconn->adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] HTTP server request link created",
- hconn->conn_id, hconn->out_link_id);
- }
-
- if (!hconn->http_conn) {
- h1_codec_config_t config = {0};
- config.type = HTTP1_CONN_SERVER;
- config.tx_buffers = _server_tx_buffers_cb;
- config.tx_stream_data = _server_tx_stream_data_cb;
- config.rx_request = _server_rx_request_cb;
- config.rx_response = _server_rx_response_cb;
- config.rx_header = _server_rx_header_cb;
- config.rx_headers_done = _server_rx_headers_done_cb;
- config.rx_body = _server_rx_body_cb;
- config.rx_done = _server_rx_done_cb;
- config.request_complete = _server_request_complete_cb;
- hconn->http_conn = h1_codec_connection(&config, hconn);
- }
-}
-
-
-// Tear down the qdr links and the codec. This is called when the
-// connection to the server has dropped and cannot be re-established in a
-// timely manner.
-//
-static void _teardown_server_links(qdr_http1_connection_t *hconn)
-{
- _server_request_t *hreq = (_server_request_t*) DEQ_HEAD(hconn->requests);
- while (hreq) {
- _server_request_free(hreq);
- hreq = (_server_request_t*) DEQ_HEAD(hconn->requests);
- }
- h1_codec_connection_free(hconn->http_conn);
- hconn->http_conn = 0;
-
- if (hconn->out_link) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Closing outgoing HTTP link",
- hconn->conn_id, hconn->out_link_id);
- qdr_link_set_context(hconn->out_link, 0);
- qdr_link_detach(hconn->out_link, QD_CLOSED, 0);
- hconn->out_link = 0;
- }
-
- if (hconn->in_link) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] Closing incoming HTTP link",
- hconn->conn_id, hconn->in_link_id);
- qdr_link_set_context(hconn->in_link, 0);
- qdr_link_detach(hconn->in_link, QD_CLOSED, 0);
- hconn->in_link = 0;
- }
-}
-
-
-// Reconnection timer handler.
-// This timer can be scheduled either by the event loop during the
-// PN_RAW_CONNECTION_DISCONNECT event or by the core thread via
-// _core_connection_activate_CT in http1_adaptor.c. Since timers do not run
-// concurrently this handler is guaranteed never to collide with itself. Once
-// hconn->raw_conn is set to zero by the disconnect handler it will remain zero
-// until this handler creates a new raw connection.
-//
-static void _do_reconnect(void *context)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) context;
- uint64_t conn_id = hconn->conn_id;
-
- // while timers do not run concurrently it is possible to reschedule them
- // via another thread while the timer handler is running, resulting in this
- // handler running twice
- sys_mutex_lock(qdr_http1_adaptor->lock);
- if (hconn->raw_conn) {
- sys_mutex_unlock(qdr_http1_adaptor->lock);
- return; // already ran
- }
- sys_mutex_unlock(qdr_http1_adaptor->lock);
-
- // handle any qdr_connection_t processing requests that occurred since
- // this raw connection dropped.
- while (hconn->qdr_conn && qdr_connection_process(hconn->qdr_conn))
- ;
-
- if (!hconn->qdr_conn) {
- // the qdr_connection_t has been closed
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"] HTTP/1.x server connection closed", hconn->conn_id);
- qdr_http1_connection_free(hconn);
- return;
- }
-
- _process_request((_server_request_t*) DEQ_HEAD(hconn->requests));
-
- if (hconn->admin_status == QD_CONN_ADMIN_ENABLED) {
-
- // Do not attempt to re-connect if the current request is still in
- // progress. This happens when the server has closed the connection before
- // the request message has fully arrived (!rx_complete).
- // qdr_connection_process() will continue to invoke the
- // qdr_http1_server_core_link_deliver callback until the request message is
- // complete.
-
- // false positive: head request is removed before it is freed, null is passed
- /* coverity[pass_freed_arg] */
- if (!_is_request_in_progress((_server_request_t*) DEQ_HEAD(hconn->requests))) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"] Connecting to HTTP server...", conn_id);
- sys_mutex_lock(qdr_http1_adaptor->lock);
- hconn->raw_conn = pn_raw_connection();
- pn_raw_connection_set_context(hconn->raw_conn, &hconn->handler_context);
- // this next call may immediately reschedule the connection on another I/O
- // thread. After this call hconn may no longer be valid!
- pn_proactor_raw_connect(qd_server_proactor(hconn->qd_server), hconn->raw_conn, hconn->cfg.host_port);
- sys_mutex_unlock(qdr_http1_adaptor->lock);
- }
- }
-}
-
-static void _accept_and_settle_request(_server_request_t *hreq)
-{
- qdr_delivery_remote_state_updated(qdr_http1_adaptor->core,
- hreq->request_dlv,
- hreq->request_dispo,
- true, // settled
- 0, // delivery state
- false);
- // can now release the delivery
- assert(DEQ_IS_EMPTY(hreq->out_data)); // expect no held references to message data
- qdr_delivery_set_context(hreq->request_dlv, 0);
- qdr_delivery_decref(qdr_http1_adaptor->core, hreq->request_dlv, "HTTP1 adaptor request settled");
- hreq->request_dlv = 0;
-
- hreq->request_settled = true;
-}
-
-
-// handle PN_RAW_CONNECTION_READ
-static int _handle_conn_read_event(qdr_http1_connection_t *hconn)
-{
- int error = 0;
- qd_buffer_list_t blist;
-
- uintmax_t length = qdr_http1_get_read_buffers(hconn, &blist);
-
- if (length) {
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Read %"PRIuMAX" bytes from server (%zu buffers)",
- hconn->conn_id, hconn->in_link_id, length, DEQ_SIZE(blist));
-
- if (HTTP1_DUMP_BUFFERS) {
- fprintf(stdout, "\nServer raw buffer READ %"PRIuMAX" total octets\n", length);
- qd_buffer_t *bb = DEQ_HEAD(blist);
- while (bb) {
- fprintf(stdout, " buffer='%.*s'\n", (int)qd_buffer_size(bb), (char*)&bb[1]);
- bb = DEQ_NEXT(bb);
- }
- fflush(stdout);
- }
-
- hconn->in_http1_octets += length;
- error = h1_codec_connection_rx_data(hconn->http_conn, &blist, length);
- }
- return error;
-}
-
-
-// handle PN_RAW_CONNECTION_NEED_READ_BUFFERS
-static void _handle_conn_need_read_buffers(qdr_http1_connection_t *hconn)
-{
- // @TODO(kgiusti): backpressure if no credit
- // if (hconn->in_link_credit > 0 */)
- int granted = qdr_http1_grant_read_buffers(hconn);
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"] %d read buffers granted",
- hconn->conn_id, granted);
-}
-
-
-// Proton Raw Connection Events
-//
-static void _handle_connection_events(pn_event_t *e, qd_server_t *qd_server, void *context)
-{
- qdr_http1_connection_t *hconn = (qdr_http1_connection_t*) context;
- qd_log_source_t *log = qdr_http1_adaptor->log;
-
- if (!hconn) return;
-
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] HTTP server proactor event %s", hconn->conn_id, pn_event_type_name(pn_event_type(e)));
-
- switch (pn_event_type(e)) {
-
- case PN_RAW_CONNECTION_CONNECTED: {
- if (hconn->oper_status == QD_CONN_OPER_DOWN) {
- hconn->oper_status = QD_CONN_OPER_UP;
- qd_log(log, QD_LOG_INFO, "[C%"PRIu64"] HTTP/1.x server %s connection established",
- hconn->conn_id, hconn->cfg.host_port);
- }
- hconn->server.link_timeout = 0;
- _setup_server_links(hconn);
- while (qdr_connection_process(hconn->qdr_conn)) {}
- break;
- }
- case PN_RAW_CONNECTION_CLOSED_READ: {
- if (hconn->q2_blocked) {
- hconn->q2_blocked = false;
- // drain any pending buffers blocked by Q2
- _handle_conn_read_event(hconn);
- }
- // notify the codec so it can complete the current response
- // message (response body terminated on connection closed)
- h1_codec_connection_rx_closed(hconn->http_conn);
- pn_raw_connection_close(hconn->raw_conn);
- break;
- }
-
- case PN_RAW_CONNECTION_CLOSED_WRITE: {
- // discard any remaining outgoing request message data
- _server_request_t *hreq = (_server_request_t*) DEQ_HEAD(hconn->requests);
- if (_is_request_in_progress(hreq)) {
- hreq->request_discard = true;
- qdr_http1_out_data_cleanup(&hreq->out_data);
- }
- pn_raw_connection_close(hconn->raw_conn);
- break;
- }
- case PN_RAW_CONNECTION_DISCONNECTED: {
- pn_raw_connection_set_context(hconn->raw_conn, 0);
-
- // Check for a request that is in-progress - it needs to be cancelled.
- // However there is an exception: the server has completed sending a
- // response message and closed the connection, but the outgoing request
- // message has not completed (example: a streaming POST that has been
- // rejected by the server). In this case wait until the request message
- // has fully arrived from the core.
-
- _server_request_t *hreq = (_server_request_t*) DEQ_HEAD(hconn->requests);
- if (_is_request_in_progress(hreq) && !hreq->response_complete)
- _cancel_request(hreq);
- _process_request(hreq);
-
- //
- // Try to reconnect to the server. Leave the links intact so pending
- // requests are not aborted. If we fail to reconnect after
- // LINK_TIMEOUT_MSECS drop the links to prevent additional request from
- // arriving.
- //
-
- bool reconnect = false;
- if (hconn->admin_status == QD_CONN_ADMIN_ENABLED && hconn->qdr_conn) {
- if (hconn->server.link_timeout == 0) {
- hconn->server.link_timeout = qd_timer_now() + LINK_TIMEOUT_MSEC;
- hconn->server.reconnect_pause = 0;
- } else {
- if ((qd_timer_now() - hconn->server.link_timeout) >= 0) {
- _teardown_server_links(hconn);
- // at this point we've unbound the service address so no
- // more messages will be sent to us. Notify meatspace:
- if (hconn->oper_status == QD_CONN_OPER_UP) {
- hconn->oper_status = QD_CONN_OPER_DOWN;
- qd_log(log, QD_LOG_INFO, "[C%"PRIu64"] HTTP/1.x server %s disconnected",
- hconn->conn_id, hconn->cfg.host_port);
- }
- }
- if (hconn->server.reconnect_pause < RETRY_MAX_PAUSE_MSEC)
- hconn->server.reconnect_pause += RETRY_PAUSE_MSEC;
- }
- reconnect = true;
- }
-
- // prevent core activation
- sys_mutex_lock(qdr_http1_adaptor->lock);
- hconn->raw_conn = 0;
- if (reconnect && hconn->server.reconnect_timer) {
- qd_timer_schedule(hconn->server.reconnect_timer, hconn->server.reconnect_pause);
- sys_mutex_unlock(qdr_http1_adaptor->lock);
- // do not manipulate hconn further as it may now be processed by the
- // timer thread
- return;
- }
- sys_mutex_unlock(qdr_http1_adaptor->lock);
- break;
- }
- case PN_RAW_CONNECTION_NEED_WRITE_BUFFERS: {
- _send_request_message((_server_request_t*) DEQ_HEAD(hconn->requests));
- break;
- }
- case PN_RAW_CONNECTION_NEED_READ_BUFFERS: {
- _handle_conn_need_read_buffers(hconn);
- break;
- }
- case PN_RAW_CONNECTION_WAKE: {
- int error = 0;
- qd_log(log, QD_LOG_DEBUG, "[C%"PRIu64"] Wake-up", hconn->conn_id);
-
- if (sys_atomic_set(&hconn->q2_restart, 0)) {
- // note: unit tests grep for this log!
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] server link unblocked from Q2 limit", hconn->conn_id);
- hconn->q2_blocked = false;
- error = _handle_conn_read_event(hconn); // restart receiving
- if (!error)
- // room for more incoming data
- _handle_conn_need_read_buffers(hconn);
- }
-
- while (qdr_connection_process(hconn->qdr_conn)) {}
-
- if (error)
- qdr_http1_close_connection(hconn, "Incoming response message failed to parse");
-
- qd_log(log, QD_LOG_DEBUG, "[C%"PRIu64"] Connection processing complete", hconn->conn_id);
- break;
- }
- case PN_RAW_CONNECTION_READ: {
- if (!hconn->q2_blocked) {
- int error = _handle_conn_read_event(hconn);
- if (error)
- qdr_http1_close_connection(hconn, "Incoming response message failed to parse");
- }
- break;
- }
- case PN_RAW_CONNECTION_WRITTEN: {
- qdr_http1_free_written_buffers(hconn);
- break;
- }
- default:
- break;
- }
-
- //
- // After each event check connection and request status
- //
- if (!hconn->qdr_conn) {
- qd_log(log, QD_LOG_DEBUG, "[C%"PRIu64"] HTTP/1.x server connection closed", hconn->conn_id);
- qdr_http1_connection_free(hconn);
-
- } else {
- bool need_close = _process_request((_server_request_t*) DEQ_HEAD(hconn->requests));
- if (need_close) {
- qd_log(log, QD_LOG_DEBUG, "[C%"PRIu64"] HTTP Request requires connection close", hconn->conn_id);
- qdr_http1_close_connection(hconn, 0);
- }
- }
-}
-
-
-// Check the head request for completion. Return true if the connection must be
-// closed before starting the next request.
-static bool _process_request(_server_request_t *hreq)
-{
- bool need_close = false;
-
- if (!hreq)
- return need_close;
-
- assert(DEQ_PREV(&hreq->base) == 0); // preserve order!
-
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- if (hreq->cancelled) {
-
- if (hreq->request_dlv) {
-
- if ((!hreq->request_acked || !hreq->request_settled) &&
- hconn->cfg.aggregation == QD_AGGREGATION_NONE) {
-
- if (!hreq->request_dispo || hreq->request_dispo == PN_ACCEPTED)
- hreq->request_dispo = (hreq->base.out_http1_octets > 0
- ? PN_MODIFIED : PN_RELEASED);
-
- qd_message_set_send_complete(qdr_delivery_message(hreq->request_dlv));
- qdr_link_complete_sent_message(qdr_http1_adaptor->core, hconn->out_link);
- qdr_delivery_remote_state_updated(qdr_http1_adaptor->core,
- hreq->request_dlv,
- hreq->request_dispo,
- true, // settled
- 0, // delivery state
- false);
- hreq->request_acked = hreq->request_settled = true;
- }
- }
-
- // drop in flight responses
- _server_response_msg_t *rmsg = DEQ_HEAD(hreq->responses);
- while (rmsg) {
- if (rmsg->dlv) {
- qd_message_set_receive_complete(qdr_delivery_message(rmsg->dlv));
- qdr_delivery_set_aborted(rmsg->dlv);
- qdr_delivery_continue(qdr_http1_adaptor->core, rmsg->dlv, true);
- }
- _server_response_msg_free(hreq, rmsg);
- rmsg = DEQ_HEAD(hreq->responses);
- }
-
- // it is safe to keep the connection up if this request has never been
- // written to the connection, otherwise the state of the connection is
- // unknown so close it
-
- if (hreq->base.out_http1_octets > 0)
- need_close = true;
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE, "[C%"PRIu64"] HTTP request msg-id=%"PRIu64" cancelled",
- hconn->conn_id, hreq->base.msg_id);
- _server_request_free(hreq);
-
- if (hconn->out_link)
- qdr_link_flow(qdr_http1_adaptor->core, hconn->out_link, 1, false);
-
- } else if (hreq->codec_completed) {
-
- // The request message has been fully encoded and the response msg(s)
- // have been completely received. The terminal disposition for the
- // request message delivery can be set now since the server is done
- // responding. The request disposition can be settled after all the
- // response messages have been delivered to the core.
-
- // hreq->out_data.fifo ==> request message written to raw conn
- // DEQ_IS_EMPTY(hreq->responses)
- if ((!hreq->request_acked || (!hreq->request_settled
- && DEQ_IS_EMPTY(hreq->responses)))
- && hconn->cfg.aggregation == QD_AGGREGATION_NONE) {
-
- assert(hreq->request_dlv);
- assert(hreq->request_dispo == PN_ACCEPTED);
- hreq->request_settled = DEQ_IS_EMPTY(hreq->responses);
-
- if (!hreq->request_acked) {
- qd_message_set_send_complete(qdr_delivery_message(hreq->request_dlv));
- qdr_link_complete_sent_message(qdr_http1_adaptor->core, hconn->out_link);
- }
- qdr_delivery_remote_state_updated(qdr_http1_adaptor->core,
- hreq->request_dlv,
- hreq->request_dispo,
- hreq->request_settled,
- 0, // delivery state
- false);
- hreq->request_acked = true;
- }
-
- if (hreq->request_acked && hreq->request_settled && DEQ_SIZE(hreq->out_data) == 0) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"] HTTP request msg-id=%"PRIu64" completed!",
- hconn->conn_id, hreq->base.msg_id);
- _server_request_free(hreq);
-
- if (hconn->out_link)
- qdr_link_flow(qdr_http1_adaptor->core, hconn->out_link, 1, false);
- }
- }
-
- return need_close;
-}
-
-
-//////////////////////////////////////////////////////////////////////
-// HTTP/1.x Encoder/Decoder Callbacks
-//////////////////////////////////////////////////////////////////////
-
-
-// Encoder has a buffer list to send to the server
-//
-static void _server_tx_buffers_cb(h1_codec_request_state_t *hrs, qd_buffer_list_t *blist, unsigned int len)
-{
- _server_request_t *hreq = (_server_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- if (hreq->request_discard)
- qd_buffer_list_free_buffers(blist);
- else {
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Sending %u octets to server",
- hconn->conn_id, hconn->out_link_id, len);
- qdr_http1_enqueue_buffer_list(&hreq->out_data, blist, len);
- }
-}
-
-
-// Encoder has body data to send to the server
-//
-static void _server_tx_stream_data_cb(h1_codec_request_state_t *hrs, qd_message_stream_data_t *stream_data)
-{
- _server_request_t *hreq = (_server_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- if (hreq->request_discard)
- qd_message_stream_data_release(stream_data);
- else {
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Sending body data to server",
- hconn->conn_id, hconn->out_link_id);
- qdr_http1_enqueue_stream_data(&hreq->out_data, stream_data);
- }
-}
-
-
-// Server will not be sending us HTTP requests
-//
-static int _server_rx_request_cb(h1_codec_request_state_t *hrs,
- const char *method,
- const char *target,
- uint32_t version_major,
- uint32_t version_minor)
-{
- _server_request_t *hreq = (_server_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_ERROR,
- "[C%"PRIu64"][L%"PRIu64"] Spurious HTTP request received from server",
- hconn->conn_id, hconn->in_link_id);
- return HTTP1_STATUS_BAD_REQ;
-}
-
-
-// called when decoding an HTTP response from the server.
-//
-static int _server_rx_response_cb(h1_codec_request_state_t *hrs,
- int status_code,
- const char *reason_phrase,
- uint32_t version_major,
- uint32_t version_minor)
-{
- _server_request_t *hreq = (_server_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- // expected to be in-order
- assert(hreq && hreq == (_server_request_t*) DEQ_HEAD(hconn->requests));
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP msg_id=%"PRIu64" response received: status=%d phrase=%s version=%"PRIi32".%"PRIi32,
- hconn->conn_id, hconn->in_link_id, hreq->base.msg_id, status_code, reason_phrase ? reason_phrase : "<NONE>",
- version_major, version_minor);
-
- if (hconn->cfg.event_channel) {
- return 0;
- }
-
- _server_response_msg_t *rmsg = new__server_response_msg_t();
- ZERO(rmsg);
- rmsg->hreq = hreq;
- DEQ_INSERT_TAIL(hreq->responses, rmsg);
-
- rmsg->msg_props = qd_compose(QD_PERFORMATIVE_APPLICATION_PROPERTIES, 0);
- qd_compose_start_map(rmsg->msg_props);
- {
- char temp[64];
- snprintf(temp, sizeof(temp), "%"PRIi32".%"PRIi32, version_major, version_minor);
- qd_compose_insert_string(rmsg->msg_props, VERSION_PROP_KEY);
- qd_compose_insert_string(rmsg->msg_props, temp);
-
- if (reason_phrase) {
- qd_compose_insert_string(rmsg->msg_props, REASON_PROP_KEY);
- qd_compose_insert_string(rmsg->msg_props, reason_phrase);
- }
-
- qd_compose_insert_string(rmsg->msg_props, PATH_PROP_KEY);
- qd_compose_insert_string(rmsg->msg_props, h1_codec_request_state_target(hrs));
- }
-
- hreq->response_complete = false;
- return 0;
-}
-
-
-// called for each decoded HTTP header.
-//
-static int _server_rx_header_cb(h1_codec_request_state_t *hrs, const char *key, const char *value)
-{
- _server_request_t *hreq = (_server_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"]L%"PRIu64"] HTTP response header received: key='%s' value='%s'",
- hconn->conn_id, hconn->in_link_id, key, value);
-
- if (hconn->cfg.event_channel) {
- return 0;
- }
-
- // expect: running incoming request at tail
- _server_response_msg_t *rmsg = DEQ_TAIL(hreq->responses);
- assert(rmsg);
-
- // We need to filter the connection header out
- // @TODO(kgiusti): also have to remove headers given in value!
- if (strcasecmp(key, "connection") != 0) {
- qd_compose_insert_symbol(rmsg->msg_props, key);
- qd_compose_insert_string(rmsg->msg_props, value);
- }
-
- return 0;
-}
-
-
-// called after the last header is decoded, before decoding any body data.
-//
-static int _server_rx_headers_done_cb(h1_codec_request_state_t *hrs, bool has_body)
-{
- _server_request_t *hreq = (_server_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP response headers done.",
- hconn->conn_id, hconn->in_link_id);
-
- if (hconn->cfg.event_channel) {
- return 0;
- }
-
- // expect: running incoming request at tail
- _server_response_msg_t *rmsg = DEQ_TAIL(hreq->responses);
- assert(rmsg && !rmsg->msg);
-
- // start building the AMQP message
-
- rmsg->msg = qd_message();
-
- qd_composed_field_t *hdrs = qd_compose(QD_PERFORMATIVE_HEADER, 0);
- qd_compose_start_list(hdrs);
- qd_compose_insert_bool(hdrs, 0); // durable
- qd_compose_insert_null(hdrs); // priority
- //qd_compose_insert_null(hdrs); // ttl
- //qd_compose_insert_bool(hdrs, 0); // first-acquirer
- //qd_compose_insert_uint(hdrs, 0); // delivery-count
- qd_compose_end_list(hdrs);
-
- qd_composed_field_t *props = qd_compose(QD_PERFORMATIVE_PROPERTIES, hdrs);
- qd_compose_start_list(props);
- qd_compose_insert_null(props); // message-id
- qd_compose_insert_null(props); // user-id
- qd_compose_insert_string(props, hreq->base.response_addr); // to
- {
- // subject:
- char u32_str[64];
- snprintf(u32_str, sizeof(u32_str), "%"PRIu32, h1_codec_request_state_response_code(hrs));
- qd_compose_insert_string(props, u32_str);
- }
- qd_compose_insert_null(props); // reply-to
- qd_compose_insert_ulong(props, hreq->base.msg_id); // correlation-id
- qd_compose_insert_null(props); // content-type
- qd_compose_insert_null(props); // content-encoding
- qd_compose_insert_null(props); // absolute-expiry-time
- qd_compose_insert_null(props); // creation-time
- qd_compose_insert_string(props, hconn->cfg.site); // group-id
- qd_compose_end_list(props);
-
- qd_compose_end_map(rmsg->msg_props);
-
- qd_message_compose_3(rmsg->msg, props, rmsg->msg_props, !has_body);
- qd_compose_free(props);
- qd_compose_free(rmsg->msg_props);
- rmsg->msg_props = 0;
-
- // future-proof: ensure the message headers have not caused Q2
- // blocking. We only check for Q2 events while adding body data.
- assert(!qd_message_is_Q2_blocked(rmsg->msg));
-
- qd_alloc_safe_ptr_t hconn_sp = QD_SAFE_PTR_INIT(hconn);
- qd_message_set_q2_unblocked_handler(rmsg->msg, qdr_http1_q2_unblocked_handler, hconn_sp);
-
- // start delivery if possible
- if (hconn->in_link_credit > 0 && rmsg == DEQ_HEAD(hreq->responses)) {
- hconn->in_link_credit -= 1;
-
- qd_log(hconn->adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Delivering msg-id=%"PRIu64" response to router addr=%s",
- hconn->conn_id, hconn->in_link_id, hreq->base.msg_id, hreq->base.response_addr);
-
- qd_iterator_t *addr = qd_message_field_iterator(rmsg->msg, QD_FIELD_TO);
- assert(addr);
- qd_iterator_reset_view(addr, ITER_VIEW_ADDRESS_HASH);
- rmsg->dlv = qdr_link_deliver_to(hconn->in_link, rmsg->msg, 0, addr, false, 0, 0, 0, 0);
- qdr_delivery_set_context(rmsg->dlv, (void*) hreq);
- rmsg->msg = 0; // now owned by delivery
- }
-
- return 0;
-}
-
-
-// Called with decoded body data. This may be called multiple times as body
-// data becomes available.
-//
-static int _server_rx_body_cb(h1_codec_request_state_t *hrs, qd_buffer_list_t *body, size_t len,
- bool more)
-{
- _server_request_t *hreq = (_server_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
- bool q2_blocked = false;
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP response body received len=%zu.",
- hconn->conn_id, hconn->in_link_id, len);
-
- if (hconn->cfg.event_channel) {
- qd_buffer_list_free_buffers(body);
- return 0;
- }
-
- _server_response_msg_t *rmsg = DEQ_TAIL(hreq->responses);
- if (rmsg->discard) {
- qd_buffer_list_free_buffers(body);
- return 0;
- }
-
- qd_message_t *msg = rmsg->msg ? rmsg->msg : qdr_delivery_message(rmsg->dlv);
- qd_message_stream_data_append(msg, body, &q2_blocked);
- hconn->q2_blocked = hconn->q2_blocked || q2_blocked;
- if (q2_blocked) {
- // note: unit tests grep for this log!
- if (rmsg->dlv)
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE, DLV_FMT" server link blocked on Q2 limit", DLV_ARGS(rmsg->dlv));
- else
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE, "[C%"PRIu64"] server link blocked on Q2 limit", hconn->conn_id);
- }
-
- //
- // Notify the router that more data is ready to be pushed out on the delivery
- //
- if (!more)
- qd_message_set_receive_complete(msg);
-
- if (rmsg->dlv)
- qdr_delivery_continue(qdr_http1_adaptor->core, rmsg->dlv, false);
-
- return 0;
-}
-
-// Called at the completion of response decoding.
-//
-static void _server_rx_done_cb(h1_codec_request_state_t *hrs)
-{
- _server_request_t *hreq = (_server_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
- if (hconn->cfg.event_channel) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP response message msg-id=%"PRIu64" decoding complete.",
- hconn->conn_id, hconn->in_link_id, hreq->base.msg_id);
- hreq->response_complete = true;
- return;
- }
- _server_response_msg_t *rmsg = DEQ_TAIL(hreq->responses);
-
- qd_message_t *msg = rmsg->msg ? rmsg->msg : qdr_delivery_message(rmsg->dlv);
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] HTTP response message msg-id=%"PRIu64" decoding complete.",
- hconn->conn_id, hconn->in_link_id, hreq->base.msg_id);
-
- rmsg->rx_complete = true;
-
- if (!qd_message_receive_complete(msg)) {
- qd_message_set_receive_complete(msg);
- if (rmsg->dlv) {
- qdr_delivery_continue(qdr_http1_adaptor->core, rmsg->dlv, false);
- }
- }
-
- if (rmsg->dlv && hconn->cfg.aggregation == QD_AGGREGATION_NONE) {
- // We've finished the delivery, and don't care about outcome/settlement
- _server_response_msg_free(hreq, rmsg);
- }
-
- // only consider the response complete if terminal response code (!1xx)
- if (h1_codec_request_state_response_code(hrs) / 100 != 1)
- hreq->response_complete = true;
-}
-
-
-// called at the completion of a full Request/Response exchange, or as a result
-// of cancelling the request. The hrs will be deleted on return from this
-// call. Any hrs related state must be released before returning from this
-// callback.
-//
-// Note: in the case where the request had multiple response messages, this
-// call occurs when the LAST response has been completely received
-// (_server_rx_done_cb())
-//
-static void _server_request_complete_cb(h1_codec_request_state_t *hrs, bool cancelled)
-{
- _server_request_t *hreq = (_server_request_t*) h1_codec_request_state_get_context(hrs);
- qdr_http1_connection_t *hconn = hreq->base.hconn;
-
- hreq->base.stop = qd_timer_now();
- qdr_http1_record_server_request_info(qdr_http1_adaptor, &hreq->base);
- hreq->base.lib_rs = 0;
- hreq->cancelled = hreq->cancelled || cancelled;
- hreq->codec_completed = !hreq->cancelled;
-
- uint64_t in_octets, out_octets;
- h1_codec_request_state_counters(hrs, &in_octets, &out_octets);
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"] HTTP request/response %s. Octets read: %"PRIu64" written: %"PRIu64,
- hconn->conn_id,
- cancelled ? "cancelled!" : "codec done",
- in_octets, out_octets);
-}
-
-
-//////////////////////////////////////////////////////////////////////
-// Router Protocol Adapter Callbacks
-//////////////////////////////////////////////////////////////////////
-
-
-// credit has been granted - responses may now be sent to the
-// router core.
-//
-void qdr_http1_server_core_link_flow(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_link_t *link,
- int credit)
-{
- assert(link == hconn->in_link); // router only grants flow on incoming link
-
- assert(qdr_link_is_anonymous(link)); // remove me
- hconn->in_link_credit += credit;
-
- qd_log(adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Credit granted on response link: %d",
- hconn->conn_id, hconn->in_link_id, hconn->in_link_credit);
-
- if (hconn->in_link_credit > 0) {
-
- _handle_conn_need_read_buffers(hconn);
-
- // check for pending responses that are blocked for credit
-
- _server_request_t *hreq = (_server_request_t*) DEQ_HEAD(hconn->requests);
- if (hreq) {
- _server_response_msg_t *rmsg = DEQ_HEAD(hreq->responses);
- while (rmsg && rmsg->msg && hconn->in_link_credit > 0) {
- assert(!rmsg->dlv);
- hconn->in_link_credit -= 1;
-
- qd_log(adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Delivering blocked response to router addr=%s",
- hconn->conn_id, hconn->in_link_id, hreq->base.response_addr);
-
- qd_iterator_t *addr = qd_message_field_iterator(rmsg->msg, QD_FIELD_TO);
- qd_iterator_reset_view(addr, ITER_VIEW_ADDRESS_HASH);
- rmsg->dlv = qdr_link_deliver_to(hconn->in_link, rmsg->msg, 0, addr, false, 0, 0, 0, 0);
- qdr_delivery_set_context(rmsg->dlv, (void*) hreq);
- rmsg->msg = 0;
- if (!rmsg->rx_complete) {
- // stop here since response must be complete before we can deliver the next one.
- break;
- }
- if (hconn->cfg.aggregation != QD_AGGREGATION_NONE) {
- // stop here since response should not be freed until it is accepted
- break;
- }
- // else the delivery is complete no need to save it
- _server_response_msg_free(hreq, rmsg);
- rmsg = DEQ_HEAD(hreq->responses);
- }
- }
- }
-}
-
-
-// Handle disposition/settlement update for the outstanding HTTP response.
-//
-void qdr_http1_server_core_delivery_update(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_http1_request_base_t *hbase,
- qdr_delivery_t *dlv,
- uint64_t disp,
- bool settled)
-{
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- DLV_FMT" HTTP response delivery update, outcome=0x%"PRIx64"%s",
- DLV_ARGS(dlv), disp, settled ? " settled": "");
-
- if (settled) {
- if (disp && disp != PN_ACCEPTED) {
- qd_log(adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] response message was not accepted, outcome=0x%"PRIx64,
- hconn->conn_id, hconn->in_link_id, disp);
-
- // This indicates the client that originated the request is
- // unreachable. Rather than drop the connection allow the server to
- // finish the response. We'll simply discard the response data as
- // it is read from the raw connection
- _server_request_t *hreq = (_server_request_t*)hbase;
- _server_response_msg_t *rmsg = DEQ_HEAD(hreq->responses);
- while (rmsg) {
- if (rmsg->dlv == dlv) {
- rmsg->discard = true;
- qd_message_t *msg = qdr_delivery_message(dlv);
- qd_message_set_discard(msg, true);
- qd_message_Q2_holdoff_disable(msg);
- break;
- }
- rmsg = DEQ_NEXT(rmsg);
- }
- }
- }
- if (hconn->cfg.aggregation != QD_AGGREGATION_NONE) {
- _server_request_t *hreq = (_server_request_t*)hbase;
- qd_message_set_send_complete(qdr_delivery_message(hreq->request_dlv));
- qdr_link_complete_sent_message(qdr_http1_adaptor->core, hconn->out_link);
- _accept_and_settle_request(hreq);
- hreq->request_acked = true;
- qd_log(adaptor->log, QD_LOG_DEBUG, "[C%"PRIu64"][L%"PRIu64"] request accepted", hconn->conn_id, hconn->in_link_id);
- _server_response_msg_t *rmsg = DEQ_TAIL(hreq->responses);
- _server_response_msg_free(hreq, rmsg);
- }
-}
-
-
-//
-// Request message forwarding
-//
-
-
-// Create a request context for a new request in msg, which is valid to a depth
-// of at least QD_DEPTH_PROPERTIES
-//
-static _server_request_t *_create_request_context(qdr_http1_connection_t *hconn, qd_message_t *msg)
-{
- uint64_t msg_id = 0;
- char *reply_to = 0;
- bool ok = false;
- qd_parsed_field_t *msg_id_pf = 0;
-
- qd_iterator_t *msg_id_itr = qd_message_field_iterator_typed(msg, QD_FIELD_MESSAGE_ID); // ulong
- if (msg_id_itr) {
- msg_id_pf = qd_parse(msg_id_itr);
- if (msg_id_pf && qd_parse_ok(msg_id_pf)) {
- msg_id = qd_parse_as_ulong(msg_id_pf);
- ok = qd_parse_ok(msg_id_pf);
- }
- }
- qd_parse_free(msg_id_pf);
- qd_iterator_free(msg_id_itr);
-
- if (!ok) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] Rejecting message missing id.",
- hconn->conn_id, hconn->out_link_id);
- return 0;
- }
-
- qd_iterator_t *reply_to_itr = qd_message_field_iterator(msg, QD_FIELD_REPLY_TO);
- reply_to = (char*) qd_iterator_copy(reply_to_itr);
- qd_iterator_free(reply_to_itr);
-
- if (!reply_to && !hconn->cfg.event_channel) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] Rejecting message no reply-to.",
- hconn->conn_id, hconn->out_link_id);
- return 0;
- }
-
- qd_iterator_t *group_id_itr = qd_message_field_iterator(msg, QD_FIELD_GROUP_ID);
- char* group_id = (char*) qd_iterator_copy(group_id_itr);
- qd_iterator_free(group_id_itr);
-
- _server_request_t *hreq = new__server_request_t();
- ZERO(hreq);
- hreq->base.hconn = hconn;
- hreq->base.msg_id = msg_id;
- hreq->base.response_addr = reply_to;
- hreq->base.site = group_id;
- hreq->base.start = qd_timer_now();
- DEQ_INIT(hreq->out_data);
- DEQ_INIT(hreq->responses);
- DEQ_INSERT_TAIL(hconn->requests, &hreq->base);
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] New HTTP Request msg-id=%"PRIu64" reply-to=%s.",
- hconn->conn_id, hconn->out_link_id, msg_id, reply_to);
- return hreq;
-}
-
-
-// Start a new request to the server. msg has been validated to at least
-// application properties depth. Returns 0 on success.
-//
-static uint64_t _send_request_headers(_server_request_t *hreq, qd_message_t *msg)
-{
- // start encoding HTTP request. Need method, target and version
-
- qdr_http1_connection_t *hconn = hreq->base.hconn;
- char *method_str = 0;
- char *target_str = 0;
- qd_parsed_field_t *app_props = 0;
- uint32_t major = 1;
- uint32_t minor = 1;
- uint64_t outcome = 0;
-
- assert(!hreq->base.lib_rs);
- assert(qd_message_check_depth(msg, QD_DEPTH_PROPERTIES) == QD_MESSAGE_DEPTH_OK);
-
- // method is passed in the SUBJECT field
- qd_iterator_t *method_iter = qd_message_field_iterator(msg, QD_FIELD_SUBJECT);
- if (!method_iter) {
- return PN_REJECTED;
- }
-
- method_str = (char*) qd_iterator_copy(method_iter);
- qd_iterator_free(method_iter);
- if (!method_str || *method_str == 0) {
- return PN_REJECTED;
- }
-
- // target, version info and other headers are in the app properties
- qd_iterator_t *app_props_iter = qd_message_field_iterator(msg, QD_FIELD_APPLICATION_PROPERTIES);
- if (!app_props_iter) {
- outcome = PN_REJECTED;
- goto exit;
- }
-
- app_props = qd_parse(app_props_iter);
- qd_iterator_free(app_props_iter);
- if (!app_props) {
- outcome = PN_REJECTED;
- goto exit;
- }
-
- qd_parsed_field_t *ref = qd_parse_value_by_key(app_props, PATH_PROP_KEY);
- target_str = (char*) qd_iterator_copy(qd_parse_raw(ref));
- if (!target_str || *target_str == 0) {
- outcome = PN_REJECTED;
- goto exit;
- }
-
-
- // Pull the version info from the app properties (e.g. "1.1")
- ref = qd_parse_value_by_key(app_props, VERSION_PROP_KEY);
- if (ref) { // optional
- char *version_str = (char*) qd_iterator_copy(qd_parse_raw(ref));
- if (version_str)
- sscanf(version_str, "%"SCNu32".%"SCNu32, &major, &minor);
- free(version_str);
- }
-
- // done copying and converting!
-
- qd_log(hconn->adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Encoding request method=%s target=%s",
- hconn->conn_id, hconn->out_link_id, method_str, target_str);
-
- hreq->base.lib_rs = h1_codec_tx_request(hconn->http_conn, method_str, target_str, major, minor);
- if (!hreq->base.lib_rs) {
- outcome = PN_REJECTED;
- goto exit;
- }
-
- h1_codec_request_state_set_context(hreq->base.lib_rs, (void*) hreq);
-
- // now send all headers in app properties
- qd_parsed_field_t *key = qd_field_first_child(app_props);
- bool ok = true;
- while (ok && key) {
- qd_parsed_field_t *value = qd_field_next_child(key);
- if (!value)
- break;
-
- qd_iterator_t *i_key = qd_parse_raw(key);
- if (!i_key)
- break;
-
- if (hconn->cfg.host_override && qd_iterator_equal(i_key, (const unsigned char*) HOST_KEY)) {
- //if host override option is in use, write the configured
- //value rather than that submitted by client
- char *header_key = (char*) qd_iterator_copy(i_key);
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Encoding request header %s:%s",
- hconn->conn_id, hconn->out_link_id,
- header_key, hconn->cfg.host_override);
-
- ok = !h1_codec_tx_add_header(hreq->base.lib_rs, header_key, hconn->cfg.host_override);
-
- free(header_key);
-
- } else if (!qd_iterator_prefix(i_key, ":")) {
-
- // ignore the special headers added by the mapping
- qd_iterator_t *i_value = qd_parse_raw(value);
- if (!i_value)
- break;
-
- char *header_key = (char*) qd_iterator_copy(i_key);
- char *header_value = (char*) qd_iterator_copy(i_value);
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Encoding request header %s:%s",
- hconn->conn_id, hconn->out_link_id,
- header_key, header_value);
-
- ok = !h1_codec_tx_add_header(hreq->base.lib_rs, header_key, header_value);
-
- free(header_key);
- free(header_value);
- }
-
-
- key = qd_field_next_child(value);
- }
-
- if (!ok)
- outcome = PN_REJECTED;
-
-exit:
-
- free(method_str);
- free(target_str);
- qd_parse_free(app_props);
-
- return outcome;
-}
-
-
-// Encode an outbound AMQP message as an HTTP Request. Sets the request_dispo
-// when the encoding completes either successfully or in error.
-//
-static void _encode_request_message(_server_request_t *hreq)
-{
- qdr_http1_connection_t *hconn = hreq->base.hconn;
- qd_message_t *msg = qdr_delivery_message(hreq->request_dlv);
-
- if (!hreq->headers_encoded) {
- hreq->request_dispo = _send_request_headers(hreq, msg);
- hreq->headers_encoded = true;
- if (hreq->request_dispo) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] Rejecting malformed message msg-id=%"PRIu64,
- hconn->conn_id, hconn->out_link_id, hreq->base.msg_id);
- return;
- }
- }
-
- while (hreq->request_dispo == 0) {
-
- qd_message_stream_data_t *stream_data = 0;
- switch (qd_message_next_stream_data(msg, &stream_data)) {
- case QD_MESSAGE_STREAM_DATA_BODY_OK: {
-
- qd_log(hconn->adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Encoding request body data",
- hconn->conn_id, hconn->out_link_id);
-
- if (h1_codec_tx_body(hreq->base.lib_rs, stream_data)) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] body data encode failed",
- hconn->conn_id, hconn->out_link_id);
- hreq->request_dispo = PN_REJECTED;
- }
- break;
- }
-
- case QD_MESSAGE_STREAM_DATA_FOOTER_OK:
- qd_message_stream_data_release(stream_data);
- break;
-
- case QD_MESSAGE_STREAM_DATA_NO_MORE:
- // indicate this message is complete
- qd_log(hconn->adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Request %p body data encode complete",
- hconn->conn_id, hconn->out_link_id, (void*) hreq);
- hreq->request_dispo = PN_ACCEPTED;
- break;
-
- case QD_MESSAGE_STREAM_DATA_INCOMPLETE:
- return; // wait for more
-
- case QD_MESSAGE_STREAM_DATA_INVALID:
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- "[C%"PRIu64"][L%"PRIu64"] Rejecting corrupted body data.",
- hconn->conn_id, hconn->out_link_id);
- hreq->request_dispo = PN_REJECTED;
- break;
- }
- }
-}
-
-
-// encode the request message and write it out to the server.
-static void _send_request_message(_server_request_t *hreq)
-{
- if (hreq) {
- assert(DEQ_PREV(&hreq->base) == 0); // preserve order!
- qdr_http1_connection_t *hconn = hreq->base.hconn;
- if (hreq->request_dispo == 0) {
- _encode_request_message(hreq);
- switch (hreq->request_dispo) {
-
- case 0:
- // streaming, not complete
- break;
-
- case PN_ACCEPTED: {
- // completed successfully
- bool ignore = false; // used client-facing only
- h1_codec_tx_done(hreq->base.lib_rs, &ignore);
- qd_log(qdr_http1_adaptor->log, QD_LOG_DEBUG,
- "[C%"PRIu64"][L%"PRIu64"] HTTP %p request message msg-id=%"PRIu64" encoding complete",
- hconn->conn_id, hconn->out_link_id, (void*)hreq, hreq->base.msg_id);
- break;
- }
-
- default:
- // encoding failure
- _cancel_request(hreq);
- return;
- }
- }
- // write encoded data to raw conn
- _write_pending_request(hreq);
- }
-}
-
-
-// The router wants to send this delivery out the link. This is either the
-// start of a new incoming HTTP request or the continuation of an existing one.
-// Note: returning a non-zero value will cause the delivery to be settled!
-//
-uint64_t qdr_http1_server_core_link_deliver(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn,
- qdr_link_t *link,
- qdr_delivery_t *delivery,
- bool settled)
-{
- qd_message_t *msg = qdr_delivery_message(delivery);
-
- _server_request_t *hreq = (_server_request_t*) qdr_delivery_get_context(delivery);
- if (!hreq) {
-
- if (qd_message_aborted(msg)) {
- // can safely discard since it was yet to be processed
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- DLV_FMT" Discarding aborted request", DLV_ARGS(delivery));
- qd_message_set_send_complete(msg);
- qdr_link_flow(qdr_http1_adaptor->core, link, 1, false);
- return PN_REJECTED;
- }
-
- // new delivery - create new request:
- switch (qd_message_check_depth(msg, QD_DEPTH_PROPERTIES)) {
- case QD_MESSAGE_DEPTH_INCOMPLETE:
- return 0;
-
- case QD_MESSAGE_DEPTH_INVALID:
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- DLV_FMT" Malformed HTTP/1.x message", DLV_ARGS(delivery));
- qd_message_set_send_complete(msg);
- qdr_link_flow(qdr_http1_adaptor->core, link, 1, false);
- return PN_REJECTED;
-
- case QD_MESSAGE_DEPTH_OK:
- hreq = _create_request_context(hconn, msg);
- if (!hreq) {
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- DLV_FMT" Discarding malformed message.", DLV_ARGS(delivery));
- qd_message_set_send_complete(msg);
- qdr_link_flow(qdr_http1_adaptor->core, link, 1, false);
- return PN_REJECTED;
- }
-
- hreq->request_dlv = delivery;
- qdr_delivery_set_context(delivery, (void*) hreq);
- qdr_delivery_incref(delivery, "HTTP1 server referencing request delivery");
- break;
- }
-
- } else if (qd_message_aborted(msg)) {
- //
- // The client has aborted the request message. This can happen when
- // the HTTP client has dropped its connection mid-request message. If
- // this request has not yet been written to the server it can be safely
- // discard. However, if some of the request has been sent then the
- // connection to the server must be bounced in order to recover.
- //
- qd_log(qdr_http1_adaptor->log, QD_LOG_WARNING,
- DLV_FMT" Client has aborted the request", DLV_ARGS(delivery));
- _cancel_request(hreq);
- return 0;
- }
-
- // send request in the proper order
- if (DEQ_HEAD(hconn->requests) == &hreq->base)
- _send_request_message(hreq);
-
- return 0;
-}
-
-
-//
-// Misc
-//
-
-// free the response message
-//
-static void _server_response_msg_free(_server_request_t *hreq, _server_response_msg_t *rmsg)
-{
- DEQ_REMOVE(hreq->responses, rmsg);
-
- // deactivate the Q2 callback
- qd_message_t *msg = rmsg->dlv ? qdr_delivery_message(rmsg->dlv) : rmsg->msg;
- qd_message_clear_q2_unblocked_handler(msg);
-
- qd_message_free(rmsg->msg);
- qd_compose_free(rmsg->msg_props);
- if (rmsg->dlv) {
- qdr_delivery_set_context(rmsg->dlv, 0);
- qdr_delivery_decref(qdr_http1_adaptor->core, rmsg->dlv, "HTTP1 server releasing response delivery");
- }
- free__server_response_msg_t(rmsg);
-}
-
-
-// Release the request
-//
-static void _server_request_free(_server_request_t *hreq)
-{
- if (hreq) {
- qdr_http1_request_base_cleanup(&hreq->base);
- qdr_http1_out_data_cleanup(&hreq->out_data);
- if (hreq->request_dlv) {
- assert(DEQ_IS_EMPTY(hreq->out_data)); // expect no held references to message data
- qdr_delivery_set_context(hreq->request_dlv, 0);
- qdr_delivery_decref(qdr_http1_adaptor->core, hreq->request_dlv, "HTTP1 server releasing request delivery");
- hreq->request_dlv = 0;
- }
-
- _server_response_msg_t *rmsg = DEQ_HEAD(hreq->responses);
- while (rmsg) {
- _server_response_msg_free(hreq, rmsg);
- rmsg = DEQ_HEAD(hreq->responses);
- }
-
- free__server_request_t(hreq);
- }
-}
-
-
-static void _write_pending_request(_server_request_t *hreq)
-{
- if (hreq && !hreq->cancelled) {
- assert(DEQ_PREV(&hreq->base) == 0); // preserve order!
- uint64_t written = qdr_http1_write_out_data(hreq->base.hconn, &hreq->out_data);
- hreq->base.out_http1_octets += written;
- if (written)
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] %"PRIu64" request octets written to server",
- hreq->base.hconn->conn_id, hreq->base.hconn->out_link_id, written);
- }
-}
-
-
-void qdr_http1_server_conn_cleanup(qdr_http1_connection_t *hconn)
-{
- for (_server_request_t *hreq = (_server_request_t*) DEQ_HEAD(hconn->requests);
- hreq;
- hreq = (_server_request_t*) DEQ_HEAD(hconn->requests)) {
- _server_request_free(hreq);
- }
-}
-
-
-static void _cancel_request(_server_request_t *hreq)
-{
- if (!hreq->cancelled) {
-
- qd_log(qdr_http1_adaptor->log, QD_LOG_TRACE,
- "[C%"PRIu64"][L%"PRIu64"] Cancelling HTTP Request msg-id=%"PRIu64,
- hreq->base.hconn->conn_id, hreq->base.hconn->out_link_id,
- hreq->base.msg_id);
-
- if (!hreq->base.lib_rs) {
- // never even got to encoding it - manually mark it cancelled
- hreq->cancelled = true;
- } else {
- // cleanup codec state - this will call _server_request_complete_cb()
- // with cancelled = true
- h1_codec_request_state_cancel(hreq->base.lib_rs);
- }
- }
-
- // cleanup occurs at the end of the connection event handler
-}
-
-
-// handle connection close request from management
-//
-void qdr_http1_server_core_conn_close(qdr_http1_adaptor_t *adaptor,
- qdr_http1_connection_t *hconn)
-{
- // prevent activation by core thread
- sys_mutex_lock(qdr_http1_adaptor->lock);
- qdr_connection_t *qdr_conn = hconn->qdr_conn;
- qdr_connection_set_context(hconn->qdr_conn, 0);
- hconn->qdr_conn = 0;
- sys_mutex_unlock(qdr_http1_adaptor->lock);
- // the core thread can no longer activate this connection
-
- hconn->oper_status = QD_CONN_OPER_DOWN;
- _teardown_server_links(hconn);
- qdr_connection_closed(qdr_conn);
- qdr_http1_close_connection(hconn, 0);
-
- // it is expected that this callback is the final callback before returning
- // from qdr_connection_process(). Free hconn when qdr_connection_process returns.
-}
diff --git a/src/adaptors/http2/http2_adaptor.c b/src/adaptors/http2/http2_adaptor.c
deleted file mode 100644
index cdcbbc7..0000000
--- a/src/adaptors/http2/http2_adaptor.c
+++ /dev/null
@@ -1,2812 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-#include "http2_adaptor.h"
-
-#include "adaptors/http_common.h"
-
-#include "qpid/dispatch/buffer.h"
-#include "qpid/dispatch/protocol_adaptor.h"
-
-#include <proton/condition.h>
-#include <proton/listener.h>
-#include <proton/netaddr.h>
-#include <proton/proactor.h>
-#include <proton/raw_connection.h>
-
-#include <inttypes.h>
-#include <nghttp2/nghttp2.h>
-#include <pthread.h>
-#include <stdio.h>
-
-const char *PATH = ":path";
-const char *METHOD = ":method";
-const char *STATUS = ":status";
-const char *CONTENT_TYPE = "content-type";
-const char *CONTENT_ENCODING = "content-encoding";
-static const int BACKLOG = 50; /* Listening backlog */
-
-#define DEFAULT_CAPACITY 250
-#define READ_BUFFERS 4
-#define WRITE_BUFFERS 4
-#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
-
-ALLOC_DEFINE(qdr_http2_stream_data_t);
-ALLOC_DEFINE(qdr_http2_connection_t);
-ALLOC_DEFINE(qd_http2_buffer_t);
-
-typedef struct qdr_http2_adaptor_t {
- qdr_core_t *core;
- qdr_protocol_adaptor_t *adaptor;
- qd_http_listener_list_t listeners; // A list of all http2 listeners
- qd_http_connector_list_t connectors; // A list of all http2 connectors
- qd_log_source_t *log_source;
- void *callbacks;
- qd_log_source_t *protocol_log_source; // A log source for the protocol trace
- qdr_http2_connection_list_t connections;
- sys_mutex_t *lock; // protects connections, connectors, listener lists
-} qdr_http2_adaptor_t;
-
-
-static qdr_http2_adaptor_t *http2_adaptor;
-const int32_t WINDOW_SIZE = 65536;
-const int32_t MAX_FRAME_SIZE = 16384;
-
-static void handle_connection_event(pn_event_t *e, qd_server_t *qd_server, void *context);
-static void _http_record_request(qdr_http2_connection_t *conn, qdr_http2_stream_data_t *stream_data);
-static void free_http2_stream_data(qdr_http2_stream_data_t *stream_data, bool on_shutdown);
-static void clean_conn_buffs(qdr_http2_connection_t* conn);
-
-static void free_all_connection_streams(qdr_http2_connection_t *http_conn, bool on_shutdown)
-{
- // Free all the stream data associated with this connection/session.
- qdr_http2_stream_data_t *stream_data = DEQ_HEAD(http_conn->streams);
- while (stream_data) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Freeing stream in free_qdr_http2_connection", stream_data->conn->conn_id, stream_data->stream_id);
- free_http2_stream_data(stream_data, on_shutdown);
- stream_data = DEQ_HEAD(http_conn->streams);
- }
-}
-
-/**
- * All streams with id greater than the last_stream_id will be freed.
- */
-static void free_unprocessed_streams(qdr_http2_connection_t *http_conn, int32_t last_stream_id)
-{
- qdr_http2_stream_data_t *stream_data = DEQ_HEAD(http_conn->streams);
- while (stream_data) {
- int32_t stream_id = stream_data->stream_id;
-
- //
- // This stream_id is greater that the last_stream_id, this stream will not be processed by the http server
- // and hence needs to be freed.
- //
- if (stream_id > last_stream_id) {
- qdr_http2_stream_data_t *next_stream_data = DEQ_NEXT(stream_data);
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Freeing stream in free_last_id_streams", stream_data->conn->conn_id, stream_data->stream_id);
- free_http2_stream_data(stream_data, false);
- stream_data = next_stream_data;
- }
- else {
- stream_data = DEQ_NEXT(stream_data);
- }
- }
-}
-
-static void set_stream_data_delivery_flags(qdr_http2_stream_data_t * stream_data, qdr_delivery_t *dlv) {
- if (dlv == stream_data->in_dlv) {
- stream_data->in_dlv_decrefed = true;
- }
- if (dlv == stream_data->out_dlv) {
- stream_data->out_dlv_decrefed = true;
- }
-}
-
-static void advance_stream_status(qdr_http2_stream_data_t *stream_data)
-{
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Trying to move stream status", stream_data->conn->conn_id, stream_data->stream_id);
- if (stream_data->status == QD_STREAM_OPEN) {
- stream_data->status = QD_STREAM_HALF_CLOSED;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Moving stream status to QD_STREAM_HALF_CLOSED", stream_data->conn->conn_id, stream_data->stream_id);
- }
- else if (stream_data->status == QD_STREAM_HALF_CLOSED) {
- stream_data->status = QD_STREAM_FULLY_CLOSED;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Moving stream status to QD_STREAM_FULLY_CLOSED", stream_data->conn->conn_id, stream_data->stream_id);
- }
- else if (stream_data->status == QD_STREAM_FULLY_CLOSED) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Not moving stream status, stream is already QD_STREAM_FULLY_CLOSED", stream_data->conn->conn_id, stream_data->stream_id);
- }
- else {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Unknown stream status", stream_data->conn->conn_id, stream_data->stream_id);
- }
-}
-
-
-qd_http2_buffer_t *qd_http2_buffer(void)
-{
- qd_http2_buffer_t *buf = new_qd_http2_buffer_t();
- ZERO(buf);
- DEQ_ITEM_INIT(buf);
- buf->size = 0;
- return buf;
-}
-
-qd_http2_buffer_t *qd_http2_buffer_list_append(qd_http2_buffer_list_t *buflist, const uint8_t *data, size_t len)
-{
- //
- // If len is zero, there's no work to do.
- //
- if (len == 0)
- return DEQ_TAIL(*buflist);
-
- //
- // If the buffer list is empty and there's some data, add one empty buffer before we begin.
- //
- if (DEQ_SIZE(*buflist) == 0) {
- qd_http2_buffer_t *buf = qd_http2_buffer();
- DEQ_INSERT_TAIL(*buflist, buf);
- }
-
- qd_http2_buffer_t *tail = DEQ_TAIL(*buflist);
-
- while (len > 0) {
- size_t to_copy = MIN(len, qd_http2_buffer_capacity(tail));
- if (to_copy > 0) {
- memcpy(qd_http2_buffer_cursor(tail), data, to_copy);
- qd_http2_buffer_insert(tail, to_copy);
- data += to_copy;
- len -= to_copy;
- }
- if (len > 0) {
- tail = qd_http2_buffer();
- DEQ_INSERT_TAIL(*buflist, tail);
- }
- }
-
- return DEQ_TAIL(*buflist);
-}
-
-
-// Per-message callback to resume receiving after Q2 is unblocked on the
-// incoming link (to HTTP2 app). This routine runs on another I/O thread so it
-// must be thread safe and hence we use the server activation lock
-//
-static void qdr_http2_q2_unblocked_handler(const qd_alloc_safe_ptr_t context)
-{
- // prevent the conn from being deleted while running:
- sys_mutex_lock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
-
- qdr_http2_connection_t *conn = (qdr_http2_connection_t*)qd_alloc_deref_safe_ptr(&context);
- if (conn && conn->pn_raw_conn) {
- SET_ATOMIC_FLAG(&conn->q2_restart);
- pn_raw_connection_wake(conn->pn_raw_conn);
- }
-
- sys_mutex_unlock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
-}
-
-/**
- * HTTP :path is mapped to the AMQP 'to' field.
- */
-qd_composed_field_t *qd_message_compose_amqp(qdr_http2_connection_t *conn,
- qd_message_t *msg,
- const char *to,
- const char *subject,
- const char *reply_to,
- const char *content_type,
- const char *content_encoding,
- int32_t correlation_id,
- const char* group_id)
-{
- qd_composed_field_t *field = qd_compose(QD_PERFORMATIVE_HEADER, 0);
- qd_message_content_t *content = MSG_CONTENT(msg);
- if (!content) {
- qd_compose_free(field);
- return 0;
- }
- //
- // Header
- //
- qd_compose_start_list(field);
- qd_compose_insert_bool(field, 0); // durable
- qd_compose_insert_null(field); // priority
- //qd_compose_insert_null(field); // ttl
- //qd_compose_insert_bool(field, 0); // first-acquirer
- //qd_compose_insert_uint(field, 0); // delivery-count
- qd_compose_end_list(field);
-
- //
- // Properties
- //
- field = qd_compose(QD_PERFORMATIVE_PROPERTIES, field);
- qd_compose_start_list(field);
- qd_compose_insert_null(field); // message-id
- qd_compose_insert_null(field); // user-id
- if (to) {
- qd_compose_insert_string(field, to); // to
- }
- else {
- qd_compose_insert_null(field);
- }
-
- if (subject) {
- qd_compose_insert_string(field, subject); // subject
- }
- else {
- qd_compose_insert_null(field);
- }
-
- if (reply_to) {
- qd_compose_insert_string(field, reply_to); // reply-to
- }
- else {
- qd_compose_insert_null(field);
- }
-
- if (correlation_id > 0) {
- qd_compose_insert_int(field, correlation_id);
- }
- else {
- qd_compose_insert_null(field); // correlation-id
- }
-
- if (content_type) {
- qd_compose_insert_string(field, content_type); // content-type
- }
- else {
- qd_compose_insert_null(field);
- }
- if (content_encoding) {
- qd_compose_insert_string(field, content_encoding); // content-encoding
- }
- else {
- qd_compose_insert_null(field);
- }
- qd_compose_insert_null(field); // absolute-expiry-time
- qd_compose_insert_null(field); // creation-time
- if (group_id) {
- qd_compose_insert_string(field, group_id); // group-id
- } else {
- qd_compose_insert_null(field);
- }
- qd_compose_end_list(field);
-
- qd_alloc_safe_ptr_t conn_sp = QD_SAFE_PTR_INIT(conn);
- qd_message_set_q2_unblocked_handler(msg, qdr_http2_q2_unblocked_handler, conn_sp);
-
- return field;
-}
-
-static size_t write_buffers(qdr_http2_connection_t *conn)
-{
- if (!conn->pn_raw_conn)
- return 0;
-
- size_t pn_buffs_to_write = pn_raw_connection_write_buffers_capacity(conn->pn_raw_conn);
-
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] write_buffers pn_raw_connection_write_buffers_capacity=%zu", conn->conn_id, pn_buffs_to_write);
-
- size_t qd_raw_buffs_to_write = DEQ_SIZE(conn->buffs);
- size_t num_buffs = qd_raw_buffs_to_write > pn_buffs_to_write ? pn_buffs_to_write : qd_raw_buffs_to_write;
-
- if (num_buffs == 0) {
- //
- // No buffers to write, cannot proceed.
- //
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"] Written 0 buffers in write_buffers() - pn_raw_connection_write_buffers_capacity = %zu, DEQ_SIZE(conn->buffs) = %zu - returning", conn->conn_id, pn_buffs_to_write, DEQ_SIZE(conn->buffs));
- return num_buffs;
- }
-
- pn_raw_buffer_t raw_buffers[num_buffs];
- qd_http2_buffer_t *qd_http2_buff = DEQ_HEAD(conn->buffs);
-
- int i = 0;
- int total_bytes = 0;
-
- while (i < num_buffs) {
- assert (qd_http2_buff != 0);
- raw_buffers[i].bytes = (char *)qd_http2_buffer_base(qd_http2_buff);
- size_t buffer_size = qd_http2_buffer_size(qd_http2_buff);
- raw_buffers[i].capacity = buffer_size;
- raw_buffers[i].size = buffer_size;
- total_bytes += buffer_size;
- raw_buffers[i].offset = 0;
- raw_buffers[i].context = (uintptr_t) qd_http2_buff;
- DEQ_REMOVE_HEAD(conn->buffs);
- qd_http2_buff = DEQ_HEAD(conn->buffs);
- i ++;
- }
-
- size_t num_buffers_written = pn_raw_connection_write_buffers(conn->pn_raw_conn, raw_buffers, num_buffs);
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"] Written %zu buffer(s) and %i bytes in write_buffers() using pn_raw_connection_write_buffers()", conn->conn_id, num_buffers_written, total_bytes);
- assert(num_buffs == num_buffers_written);
- return num_buffers_written;
-}
-
-
-static void free_http2_stream_data(qdr_http2_stream_data_t *stream_data, bool on_shutdown)
-{
- if (!stream_data)
- return;
-
- qdr_http2_connection_t *conn = stream_data->conn;
-
- // Record the request just before freeing the stream.
- _http_record_request(conn, stream_data);
-
- if (!on_shutdown) {
- if (conn->qdr_conn && stream_data->in_link) {
- qdr_link_set_context(stream_data->in_link, 0);
- qdr_link_detach(stream_data->in_link, QD_CLOSED, 0);
- }
- if (conn->qdr_conn && stream_data->out_link) {
- qdr_link_set_context(stream_data->out_link, 0);
- qdr_link_detach(stream_data->out_link, QD_CLOSED, 0);
- }
- }
- free(stream_data->reply_to);
- qd_compose_free(stream_data->app_properties);
- qd_buffer_list_free_buffers(&stream_data->body_buffers);
- qd_compose_free(stream_data->footer_properties);
- if (DEQ_SIZE(conn->streams) > 0) {
- DEQ_REMOVE(conn->streams, stream_data);
- nghttp2_session_set_stream_user_data(conn->session, stream_data->stream_id, NULL);
- }
- free(stream_data->method);
- free(stream_data->remote_site);
- free(stream_data->request_status);
-
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Freeing stream_data in free_http2_stream_data (%lx)", conn->conn_id, stream_data->stream_id, (long) stream_data);
-
- // If the httpConnector was deleted, a client request has nowhere to go because of lack of receiver and hence credit.
- // No delivery was created. The message that was created for such a hanging request must be freed here..
- if (!stream_data->in_dlv && stream_data->message) {
- qd_message_clear_q2_unblocked_handler(stream_data->message);
- qd_message_free(stream_data->message);
- }
-
- //
- // If the client/server closed the connection abruptly, we need to release the stream_data->curr_stream_data and
- // stream_data->next_stream_data.
- // This final decref of the delivery is going to free the associated message but before this message can be freed
- // all stream data (body data) objects need to be freed. We do this here.
- //
- if (stream_data->in_dlv && !stream_data->in_dlv_decrefed) {
- qd_message_stream_data_release(stream_data->curr_stream_data);
- qd_iterator_free(stream_data->curr_stream_data_iter);
- stream_data->curr_stream_data = 0;
- stream_data->curr_stream_data_iter = 0;
-
- qd_message_stream_data_release(stream_data->next_stream_data);
- stream_data->next_stream_data = 0;
-
- qdr_delivery_decref(http2_adaptor->core, stream_data->in_dlv, "HTTP2 adaptor in_dlv - free_http2_stream_data");
- }
-
- if (stream_data->out_dlv && !stream_data->out_dlv_decrefed) {
- qd_message_stream_data_release(stream_data->curr_stream_data);
- qd_iterator_free(stream_data->curr_stream_data_iter);
- stream_data->curr_stream_data = 0;
- stream_data->curr_stream_data_iter = 0;
-
- qd_message_stream_data_release(stream_data->next_stream_data);
- stream_data->next_stream_data = 0;
- qdr_delivery_decref(http2_adaptor->core, stream_data->out_dlv, "HTTP2 adaptor out_dlv - free_http2_stream_data");
- }
-
- free_qdr_http2_stream_data_t(stream_data);
-}
-
-
-static char *get_address_string(pn_raw_connection_t *pn_raw_conn)
-{
- const pn_netaddr_t *netaddr = pn_raw_connection_remote_addr(pn_raw_conn);
- char buffer[1024];
- int len = pn_netaddr_str(netaddr, buffer, 1024);
- if (len <= 1024) {
- return strdup(buffer);
- } else {
- return strndup(buffer, 1024);
- }
-}
-
-void free_qdr_http2_connection(qdr_http2_connection_t* http_conn, bool on_shutdown)
-{
- // Free all the stream data associated with this connection/session.
- free_all_connection_streams(http_conn, on_shutdown);
- clean_conn_buffs(http_conn);
-
- if(http_conn->remote_address) {
- free(http_conn->remote_address);
- http_conn->remote_address = 0;
- }
- if (http_conn->activate_timer) {
- qd_timer_free(http_conn->activate_timer);
- http_conn->activate_timer = 0;
- }
-
- http_conn->context.context = 0;
-
- if (http_conn->session) {
- nghttp2_session_del(http_conn->session);
- }
-
- sys_mutex_lock(http2_adaptor->lock);
- DEQ_REMOVE(http2_adaptor->connections, http_conn);
- sys_mutex_unlock(http2_adaptor->lock);
-
- qd_http2_buffer_t *buff = DEQ_HEAD(http_conn->granted_read_buffs);
- while (buff) {
- DEQ_REMOVE_HEAD(http_conn->granted_read_buffs);
- free_qd_http2_buffer_t(buff);
- buff = DEQ_HEAD(http_conn->granted_read_buffs);
- }
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"] Freeing http2 connection in free_qdr_http2_connection", http_conn->conn_id);
-
- sys_atomic_destroy(&http_conn->raw_closed_read);
- sys_atomic_destroy(&http_conn->raw_closed_write);
- sys_atomic_destroy(&http_conn->q2_restart);
- sys_atomic_destroy(&http_conn->delay_buffer_write);
-
- free_qdr_http2_connection_t(http_conn);
-}
-
-static qdr_http2_stream_data_t *create_http2_stream_data(qdr_http2_connection_t *conn, int32_t stream_id)
-{
- qdr_http2_stream_data_t *stream_data = new_qdr_http2_stream_data_t();
-
- ZERO(stream_data);
- stream_data->stream_id = stream_id;
-
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Created new stream_data (%lx)", conn->conn_id, stream_id, (long) stream_data);
-
- stream_data->message = qd_message();
- qd_message_set_stream_annotation(stream_data->message, true);
- qdr_new_message_annotate(http2_adaptor->core, stream_data->message);
- stream_data->conn = conn;
- stream_data->app_properties = qd_compose(QD_PERFORMATIVE_APPLICATION_PROPERTIES, 0);
- stream_data->status = QD_STREAM_OPEN;
- DEQ_INIT(stream_data->body_buffers);
- stream_data->start = qd_timer_now();
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Creating new stream_data->app_properties=QD_PERFORMATIVE_APPLICATION_PROPERTIES", conn->conn_id, stream_id);
- qd_compose_start_map(stream_data->app_properties);
- nghttp2_session_set_stream_user_data(conn->session, stream_id, stream_data);
- DEQ_INSERT_TAIL(conn->streams, stream_data);
- stream_data->out_msg_has_body = true;
- return stream_data;
-}
-
-
-/**
- * This callback function is invoked when the nghttp2 library tells the application about the error code, and error message.
- */
-static int on_error_callback(nghttp2_session *session, int lib_error_code, const char *msg, size_t len, void *user_data)
-{
- qdr_http2_connection_t *conn = (qdr_http2_connection_t *)user_data;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_ERROR, "[C%"PRIu64"] Error generated in the on_error_callback, lib_error_code=%i, error_msg=%s", conn->conn_id, lib_error_code, msg);
- return 0;
-}
-
-
-/**
- * Callback function invoked by nghttp2_session_recv() and nghttp2_session_mem_recv() when an invalid non-DATA frame is received
- */
-static int on_invalid_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, void *user_data)
-{
- qdr_http2_connection_t *conn = (qdr_http2_connection_t *)user_data;
- int32_t stream_id = frame->hd.stream_id;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] on_invalid_frame_recv_callback", conn->conn_id, stream_id);
- return 0;
-}
-
-
-static int on_data_chunk_recv_callback(nghttp2_session *session,
- uint8_t flags,
- int32_t stream_id,
- const uint8_t *data,
- size_t len,
- void *user_data)
-{
- qdr_http2_connection_t *conn = (qdr_http2_connection_t *)user_data;
- qdr_http2_stream_data_t *stream_data = nghttp2_session_get_stream_user_data(conn->session, stream_id);
-
- if (!stream_data)
- return 0;
-
- if(stream_data->stream_force_closed)
- return 0;
-
- stream_data->bytes_in += len;
-
-
- //
- // DISPATCH-1868: If an in_dlv is present it means that the qdr_link_deliver() has already been called (delivery has already been routed)
- // in which case qd_message_stream_data_append can be called to append buffers to the message body
- // If stream_data->in_dlv = 0 but stream_data->header_and_props_composed is true, it means that the message has not been routed yet
- // but the message already has headers and properties
- // in which case the qd_message_stream_data_append() can be called to add body data to the message.
- // In many cases when the response message is streamed by a server, the entire message body can arrive before we get credit to route it.
- // We want to be able to keep collecting the incoming DATA in the message object so we can ultimately route it when the credit does ultimately arrive.
- //
- if (stream_data->in_dlv || stream_data->header_and_props_composed) {
- qd_buffer_list_t buffers;
- DEQ_INIT(buffers);
- qd_buffer_list_append(&buffers, (uint8_t *)data, len);
- // DISPATCH-1868: Part of the HTTP2 message body arrives *before* we can route the delivery. So we accumulated the body buffers
- // in the stream_data->body_buffers. But before the rest of the HTTP2 data arrives, we got credit to send the delivery
- // and we have an in_dlv object now. Now, we take the buffers that were added previously to stream_data->body_buffers and call qd_message_stream_data_append
- bool q2_blocked1 = false;
- if (DEQ_SIZE(stream_data->body_buffers) > 0) {
- if (!stream_data->body_data_added_to_msg) {
- qd_message_stream_data_append(stream_data->message, &stream_data->body_buffers, &q2_blocked1);
- }
- }
- bool q2_blocked2 = false;
- qd_message_stream_data_append(stream_data->message, &buffers, &q2_blocked2);
- stream_data->body_data_added_to_msg = true;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 DATA on_data_chunk_recv_callback qd_compose_insert_binary_buffers into stream_data->message", conn->conn_id, stream_id);
- conn->q2_blocked = conn->q2_blocked || q2_blocked1 || q2_blocked2;
-
- if (conn->q2_blocked) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] q2 is blocked on this connection", conn->conn_id);
- }
- }
- else {
- // Keep inserting buffers to stream_data->body_buffers.
- qd_buffer_list_append(&stream_data->body_buffers, (uint8_t *)data, len);
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 DATA on_data_chunk_recv_callback qd_compose_insert_binary_buffers into stream_data->body_buffers", conn->conn_id, stream_id);
- }
-
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 DATA on_data_chunk_recv_callback data length %zu", conn->conn_id, stream_id, len);
-
- // Calling this here to send out any WINDOW_UPDATE frames that might be necessary.
- // The only function that nghttp2 calls if it wants to send data is the send_callback.
- // The only function that calls send_callback is nghttp2_session_send
- nghttp2_session_send(conn->session);
-
- //Returning zero means success.
- return 0;
-}
-
-
-static int send_data_callback(nghttp2_session *session,
- nghttp2_frame *frame,
- const uint8_t *framehd,
- size_t length,
- nghttp2_data_source *source,
- void *user_data) {
- // The frame is a DATA frame to send. The framehd is the serialized frame header (9 bytes).
- // The length is the length of application data to send (this does not include padding)
- qdr_http2_connection_t *conn = (qdr_http2_connection_t *)user_data;
- qdr_http2_stream_data_t *stream_data = (qdr_http2_stream_data_t *)source->ptr;
-
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] send_data_callback length=%zu", conn->conn_id, stream_data->stream_id, length);
-
- int bytes_sent = 0; // This should not include the header length of 9.
- bool write_buffs = false;
- if (length) {
- qd_http2_buffer_t *tail_buff = qd_http2_buffer_list_append(&(conn->buffs), framehd, HTTP2_DATA_FRAME_HEADER_LENGTH);
- size_t tail_buff_capacity = qd_http2_buffer_capacity(tail_buff);
- if (tail_buff_capacity == 0) {
- tail_buff = qd_http2_buffer();
- DEQ_INSERT_TAIL(conn->buffs, tail_buff);
- tail_buff_capacity = qd_http2_buffer_capacity(tail_buff);
- }
- size_t bytes_to_write = length;
- while (bytes_to_write > 0) {
- uint32_t octets_remaining = qd_iterator_remaining(stream_data->curr_stream_data_iter);
- size_t len = MIN(tail_buff_capacity, bytes_to_write);
- len = MIN(len, octets_remaining);
- int copied = qd_iterator_ncopy(stream_data->curr_stream_data_iter, qd_http2_buffer_cursor(tail_buff), len);
- assert(copied == len);
- qd_http2_buffer_insert(tail_buff, len);
- octets_remaining -= copied;
- bytes_sent += copied;
- qd_iterator_trim_view(stream_data->curr_stream_data_iter, octets_remaining);
- bytes_to_write -= len;
- if (bytes_to_write > 0 && qd_http2_buffer_capacity(tail_buff) == 0) {
- tail_buff = qd_http2_buffer();
- DEQ_INSERT_TAIL(conn->buffs, tail_buff);
- tail_buff_capacity = qd_http2_buffer_capacity(tail_buff);
- }
- }
- }
- else if (length == 0 && stream_data->out_msg_data_flag_eof) {
- qd_http2_buffer_t *http2_buff = qd_http2_buffer();
- DEQ_INSERT_TAIL(conn->buffs, http2_buff);
- memcpy(qd_http2_buffer_cursor(http2_buff), framehd, HTTP2_DATA_FRAME_HEADER_LENGTH);
- qd_http2_buffer_insert(http2_buff, HTTP2_DATA_FRAME_HEADER_LENGTH);
- }
-
- //
- // If the message has a footer, don't flush the buffers now. Flush them after you write out the footer.
- //
- if (!stream_data->out_msg_has_footer) {
- write_buffs = true;
- }
-
- if (stream_data->full_payload_handled) {
- if (stream_data->curr_stream_data) {
- if (stream_data->curr_stream_data_result == QD_MESSAGE_STREAM_DATA_FOOTER_OK) {
- stream_data->footer_stream_data = stream_data->curr_stream_data;
- stream_data->footer_stream_data_iter = stream_data->curr_stream_data_iter;
- }
- else {
- qd_message_stream_data_release(stream_data->curr_stream_data);
- qd_iterator_free(stream_data->curr_stream_data_iter);
- }
- stream_data->curr_stream_data_iter = 0;
- stream_data->curr_stream_data = 0;
- }
- stream_data->payload_handled = 0;
- }
- else {
- stream_data->payload_handled += bytes_sent;
- }
-
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 send_data_callback finished, length=%zu, bytes_sent=%i, stream_data=%p", conn->conn_id, stream_data->stream_id, length, bytes_sent, (void *)stream_data);
-
- if (length) {
- assert(bytes_sent == length);
- }
-
- if (write_buffs) {
- write_buffers(conn);
- }
-
- return 0;
-
-}
-
-static ssize_t send_callback(nghttp2_session *session,
- const uint8_t *data,
- size_t length,
- int flags,
- void *user_data) {
- qdr_http2_connection_t *conn = (qdr_http2_connection_t *)user_data;
- qd_http2_buffer_list_append(&(conn->buffs), (uint8_t *)data, length);
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] HTTP2 send_callback data length %zu", conn->conn_id, length);
- if (! IS_ATOMIC_FLAG_SET(&conn->delay_buffer_write)) {
- write_buffers(conn);
- }
-
- return (ssize_t)length;
-}
-
-/**
- * This callback function is invoked with the reception of header block in HEADERS or PUSH_PROMISE is started.
- * The HEADERS frame can arrive from a client or server. We start building a new AMQP message (qd_message_t) in this callback and create the two links per stream.
- *
- * Return zero if function succeeds.
- */
-static int on_begin_headers_callback(nghttp2_session *session,
- const nghttp2_frame *frame,
- void *user_data)
-{
- qdr_http2_connection_t *conn = (qdr_http2_connection_t *)user_data;
- qdr_http2_stream_data_t *stream_data = 0;
-
- // For the client applications, frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE
- // TODO - deal with NGHTTP2_PUSH_PROMISE
- if (frame->hd.type == NGHTTP2_HEADERS) {
- if(frame->headers.cat == NGHTTP2_HCAT_REQUEST && conn->ingress) {
- if (!conn->qdr_conn) {
- return 0;
- }
-
- int32_t stream_id = frame->hd.stream_id;
- qdr_terminus_t *target = qdr_terminus(0);
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] Processing incoming HTTP2 stream with id %"PRId32"", conn->conn_id, stream_id);
- stream_data = create_http2_stream_data(conn, stream_id);
-
- //
- // For every single stream in the same connection, create -
- // 1. sending link with the configured address as the target
- //
- qdr_terminus_set_address(target, conn->config->address);
- stream_data->in_link = qdr_link_first_attach(conn->qdr_conn,
- QD_INCOMING,
- qdr_terminus(0), //qdr_terminus_t *source,
- target, //qdr_terminus_t *target,
- "http.ingress.in", //const char *name,
- 0, //const char *terminus_addr,
- false,
- NULL,
- &(stream_data->incoming_id));
- qdr_link_set_context(stream_data->in_link, stream_data);
-
- //
- // 2. dynamic receiver on which to receive back the response data for that stream.
- //
- qdr_terminus_t *dynamic_source = qdr_terminus(0);
- qdr_terminus_set_dynamic(dynamic_source);
- stream_data->out_link = qdr_link_first_attach(conn->qdr_conn,
- QD_OUTGOING, //Receiver
- dynamic_source, //qdr_terminus_t *source,
- qdr_terminus(0), //qdr_terminus_t *target,
- "http.ingress.out", //const char *name,
- 0, //const char *terminus_addr,
- false,
- NULL,
- &(stream_data->outgoing_id));
- qdr_link_set_context(stream_data->out_link, stream_data);
- }
- }
-
- return 0;
-}
-
-/**
- * nghttp2_on_header_callback: Called when nghttp2 library emits
- * single header name/value pair.
- * Collects all headers in the application properties map of the AMQP
- *
- * @return zero if function succeeds.
- */
-static int on_header_callback(nghttp2_session *session,
- const nghttp2_frame *frame,
- const uint8_t *name,
- size_t namelen,
- const uint8_t *value,
- size_t valuelen,
- uint8_t flags,
- void *user_data)
-{
- int32_t stream_id = frame->hd.stream_id;
- qdr_http2_connection_t *conn = (qdr_http2_connection_t *)user_data;
- qdr_http2_stream_data_t *stream_data = nghttp2_session_get_stream_user_data(conn->session, stream_id);
-
- switch (frame->hd.type) {
- case NGHTTP2_HEADERS: {
- if (stream_data->use_footer_properties) {
- if (!stream_data->footer_properties) {
- stream_data->footer_properties = qd_compose(QD_PERFORMATIVE_FOOTER, 0);
- qd_compose_start_map(stream_data->footer_properties);
- }
-
- qd_compose_insert_string_n(stream_data->footer_properties, (const char *)name, namelen);
- qd_compose_insert_string_n(stream_data->footer_properties, (const char *)value, valuelen);
-
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 FOOTER Incoming [%s=%s]", conn->conn_id, stream_data->stream_id, (char *)name, (char *)value);
- }
- else {
- if (strcmp(METHOD, (const char *)name) == 0) {
- stream_data->method = qd_strdup((const char *)value);
- }
- if (strcmp(STATUS, (const char *)name) == 0) {
- stream_data->request_status = qd_strdup((const char *)value);
- }
- qd_compose_insert_string_n(stream_data->app_properties, (const char *)name, namelen);
- qd_compose_insert_string_n(stream_data->app_properties, (const char *)value, valuelen);
-
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 HEADER Incoming [%s=%s]", conn->conn_id, stream_data->stream_id, (char *)name, (char *)value);
- }
-
- }
- break;
- default:
- break;
- }
- return 0;
-}
-
-
-static bool compose_and_deliver(qdr_http2_connection_t *conn, qdr_http2_stream_data_t *stream_data, bool receive_complete)
-{
- if (!stream_data->header_and_props_composed) {
- qd_composed_field_t *header_and_props = 0;
- if (conn->ingress) {
- header_and_props = qd_message_compose_amqp(conn,
- stream_data->message,
- conn->config->address, // const char *to
- stream_data->method, // const char *subject
- stream_data->reply_to, // const char *reply_to
- 0, // const char *content_type
- 0, // const char *content_encoding
- 0, // int32_t correlation_id
- conn->config->site);
- }
- else {
- header_and_props = qd_message_compose_amqp(conn,
- stream_data->message,
- stream_data->reply_to, // const char *to
- stream_data->request_status, // const char *subject
- 0, // const char *reply_to
- 0, // const char *content_type
- 0, // const char *content_encoding
- 0, // int32_t correlation_id
- conn->config->site);
- }
-
- if (receive_complete) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"][L%"PRIu64"] receive_complete = true in compose_and_deliver", conn->conn_id, stream_data->stream_id, stream_data->in_link->identity);
- bool q2_blocked;
- if (stream_data->footer_properties) {
- qd_message_compose_3(stream_data->message, header_and_props, stream_data->app_properties, receive_complete);
- qd_message_stream_data_append(stream_data->message, &stream_data->body_buffers, &q2_blocked);
- stream_data->body_data_added_to_msg = true;
-
- qd_buffer_list_t existing_buffers;
- DEQ_INIT(existing_buffers);
- qd_compose_take_buffers(stream_data->footer_properties, &existing_buffers);
- qd_message_stream_data_footer_append(stream_data->message, &existing_buffers);
- }
- else {
- qd_message_compose_3(stream_data->message, header_and_props, stream_data->app_properties, receive_complete);
- qd_message_stream_data_append(stream_data->message, &stream_data->body_buffers, &q2_blocked);
- stream_data->body_data_added_to_msg = true;
- }
-
- conn->q2_blocked = conn->q2_blocked || q2_blocked;
- if (conn->q2_blocked) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] q2 is blocked on this connection", conn->conn_id);
- }
- }
- else {
- if (DEQ_SIZE(stream_data->body_buffers) > 0) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"][L%"PRIu64"] receive_complete = false and has stream_data->body_buffers in compose_and_deliver", conn->conn_id, stream_data->stream_id, stream_data->in_link->identity);
- bool q2_blocked;
- if (stream_data->footer_properties) {
- if (!stream_data->entire_footer_arrived) {
- qd_compose_free(header_and_props);
- return false;
- }
-
- qd_message_compose_3(stream_data->message, header_and_props, stream_data->app_properties, receive_complete);
- qd_message_stream_data_append(stream_data->message, &stream_data->body_buffers, &q2_blocked);
- qd_buffer_list_t existing_buffers;
- DEQ_INIT(existing_buffers);
- qd_compose_take_buffers(stream_data->footer_properties, &existing_buffers);
- qd_message_stream_data_footer_append(stream_data->message, &existing_buffers);
- }
- else {
- qd_message_compose_3(stream_data->message, header_and_props, stream_data->app_properties, receive_complete);
- qd_message_stream_data_append(stream_data->message, &stream_data->body_buffers, &q2_blocked);
- }
- stream_data->body_data_added_to_msg = true;
- conn->q2_blocked = conn->q2_blocked || q2_blocked;
- if (conn->q2_blocked) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] q2 is blocked on this connection", conn->conn_id);
- }
- }
- else {
- if (stream_data->footer_properties) {
-
- if (!stream_data->entire_footer_arrived) {
- qd_compose_free(header_and_props);
- return false;
- }
-
- //
- // The footer has already arrived but there was no body. Insert an empty body
- //
- qd_message_compose_3(stream_data->message, header_and_props, stream_data->app_properties, receive_complete);
- qd_message_stream_data_append(stream_data->message, &stream_data->body_buffers, 0);
-
- qd_buffer_list_t existing_buffers;
- DEQ_INIT(existing_buffers);
- qd_compose_take_buffers(stream_data->footer_properties, &existing_buffers);
- qd_message_stream_data_footer_append(stream_data->message, &existing_buffers);
- stream_data->body_data_added_to_msg = true;
- }
- else {
- qd_message_compose_3(stream_data->message, header_and_props, stream_data->app_properties, receive_complete);
- stream_data->body_data_added_to_msg = false;
- }
- }
- }
-
- // The header and properties have been added. Now we can start adding BODY DATA to this message.
- stream_data->header_and_props_composed = true;
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"][L%"PRIu64"] stream_data->header_and_props_composed = true in compose_and_deliver", conn->conn_id, stream_data->stream_id, stream_data->in_link->identity);
- qd_compose_free(header_and_props);
- }
-
- if (!stream_data->in_dlv && stream_data->in_link_credit > 0) {
- //
- // Not doing an incref here since the qdr_link_deliver increfs the delivery twice
- //
- stream_data->in_dlv = qdr_link_deliver(stream_data->in_link, stream_data->message, 0, false, 0, 0, 0, 0);
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Routed delivery in compose_and_deliver (conn->ingress=%i) "DLV_FMT, conn->conn_id, stream_data->stream_id, conn->ingress, DLV_ARGS(stream_data->in_dlv));
- qdr_delivery_set_context(stream_data->in_dlv, stream_data);
- stream_data->in_link_credit -= 1;
- return true;
- }
- return false;
-}
-
-static bool route_delivery(qdr_http2_stream_data_t *stream_data, bool receive_complete)
-{
- qdr_http2_connection_t *conn = stream_data->conn;
- if (stream_data->in_dlv) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] in_dlv already present, delivery already routed", conn->conn_id, stream_data->stream_id);
- return false;
- }
-
- bool delivery_routed = false;
-
- if (conn->ingress) {
- if (stream_data->reply_to && stream_data->entire_header_arrived && !stream_data->in_dlv) {
- delivery_routed = compose_and_deliver(conn, stream_data, receive_complete);
- }
- if (!stream_data->reply_to) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"][L%"PRIu64"] stream_data->reply_to is unavailable, did not route delivery in route_delivery", conn->conn_id, stream_data->stream_id, stream_data->in_link->identity);
- }
- }
- else {
- if (stream_data->entire_header_arrived && !stream_data->in_dlv) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Calling compose_and_deliver, routing delivery", conn->conn_id, stream_data->stream_id);
- delivery_routed = compose_and_deliver(conn, stream_data, receive_complete);
- }
- }
-
- return delivery_routed;
-}
-
-static void create_settings_frame(qdr_http2_connection_t *conn)
-{
- nghttp2_settings_entry iv[4] = {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100},
- {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, WINDOW_SIZE},
- {NGHTTP2_SETTINGS_MAX_FRAME_SIZE, MAX_FRAME_SIZE},
- {NGHTTP2_SETTINGS_ENABLE_PUSH, 0}};
-
- // You must call nghttp2_session_send after calling nghttp2_submit_settings
- int rv = nghttp2_submit_settings(conn->session, NGHTTP2_FLAG_NONE, iv, ARRLEN(iv));
- if (rv != 0) {
- qd_log(http2_adaptor->log_source, QD_LOG_ERROR, "[C%"PRIu64"] Fatal error sending settings frame, rv=%i", conn->conn_id, rv);
- return;
- }
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"] Initial SETTINGS frame sent", conn->conn_id);
-}
-
-static void send_settings_frame(qdr_http2_connection_t *conn)
-{
- create_settings_frame(conn);
- nghttp2_session_send(conn->session);
- write_buffers(conn);
-}
-
-static void _http_record_request(qdr_http2_connection_t *conn, qdr_http2_stream_data_t *stream_data)
-{
- stream_data->stop = qd_timer_now();
-
- bool free_remote_addr = false;
- char *remote_addr;
- if (conn->ingress) {
- remote_addr = qd_get_host_from_host_port(conn->remote_address);
- if (remote_addr) {
- free_remote_addr = true;
- } else {
- remote_addr = conn->remote_address;
- }
- } else {
- remote_addr = conn->config?conn->config->host:0;
- }
- qd_http_record_request(http2_adaptor->core,
- stream_data->method,
- stream_data->request_status?atoi(stream_data->request_status):0,
- conn->config?conn->config->address:0,
- remote_addr, conn->config?conn->config->site:0,
- stream_data->remote_site,
- conn->ingress, stream_data->bytes_in, stream_data->bytes_out,
- stream_data->stop && stream_data->start ? stream_data->stop - stream_data->start : 0);
- if (free_remote_addr) {
- free(remote_addr);
- }
-}
-
-static int on_frame_recv_callback(nghttp2_session *session,
- const nghttp2_frame *frame,
- void *user_data)
-{
- qdr_http2_connection_t *conn = (qdr_http2_connection_t *)user_data;
- int32_t stream_id = frame->hd.stream_id;
- qdr_http2_stream_data_t *stream_data = nghttp2_session_get_stream_user_data(conn->session, stream_id);
-
- switch (frame->hd.type) {
- case NGHTTP2_GOAWAY: {
- //
- // A GOAWAY frame has been received from the HTTP2 server. Usually a server sends a GOAWAY but nothing prevents the client from sending one.
- //
- // "The GOAWAY frame is used to initiate shutdown of a connection or to signal serious error conditions. GOAWAY allows an
- // endpoint to gracefully stop accepting new streams while still
- // finishing processing of previously established streams. This enables administrative actions, like server maintenance.
- // Receivers of a GOAWAY frame MUST NOT open additional streams on the connection, although a new connection can be established for new streams."
- //
- // We will close any unprocessed streams on the connection. In doing so, all the outstanding deliveries on that connection will be PN_RELEASED which will in turn release all the peer
- // deliveries on the client side which will enable us to send a GOAWAY frame to the client. This is how we propagate a GOAWAY received from the server side to the client side.
- //
- // We will also close the pn_raw_connection (we will not close the qdr_connection_t and the qdr_http2_connection_t, those will still remain). This will close the TCP connection to the server
- // and will enable creation of a new connection to the server since we are not allowed to create any more streams on the connection that received the GOAWAY frame.
- //
- int32_t last_stream_id = frame->goaway.last_stream_id;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_ERROR, "[C%"PRIu64"][S%"PRId32"] GOAWAY frame received, last_stream_id=[%"PRId32"]", conn->conn_id, stream_id, last_stream_id);
- // Free all streams that are greater that the last_stream_id because the server is not going to process those streams.
- free_unprocessed_streams(conn, last_stream_id);
- conn->goaway_received = true;
- pn_raw_connection_close(conn->pn_raw_conn);
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_ERROR, "[C%"PRIu64"][S%"PRId32"] pn_raw_connection closed after GOAWAY frame received", conn->conn_id, stream_id);
- return 0;
- }
- break;
- case NGHTTP2_PING: {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 PING frame received", conn->conn_id, stream_id);
- }
- break;
- case NGHTTP2_PRIORITY: {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 PRIORITY frame received", conn->conn_id, stream_id);
- }
- break;
- case NGHTTP2_SETTINGS: {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 SETTINGS frame received", conn->conn_id, stream_id);
- }
- break;
- case NGHTTP2_WINDOW_UPDATE:
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 WINDOW_UPDATE frame received", conn->conn_id, stream_id);
- break;
- case NGHTTP2_DATA: {
-
- if (!stream_data)
- return 0;
-
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] NGHTTP2_DATA frame received", conn->conn_id, stream_id);
-
- if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
- if (!stream_data->stream_force_closed) {
- qd_message_set_receive_complete(stream_data->message);
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] NGHTTP2_DATA NGHTTP2_FLAG_END_STREAM flag received, setting receive_complete = true", conn->conn_id, stream_id);
- }
- advance_stream_status(stream_data);
- }
-
- if (stream_data->in_dlv && !stream_data->stream_force_closed) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] NGHTTP2_DATA frame received, qdr_delivery_continue "DLV_FMT, conn->conn_id, stream_id, DLV_ARGS(stream_data->in_dlv));
- qdr_delivery_continue(http2_adaptor->core, stream_data->in_dlv, false);
- }
-
- if (stream_data->out_dlv && !stream_data->disp_updated && !stream_data->out_dlv_decrefed && stream_data->status == QD_STREAM_FULLY_CLOSED ) {
- stream_data->disp_updated = true;
- qdr_delivery_remote_state_updated(http2_adaptor->core, stream_data->out_dlv, stream_data->out_dlv_local_disposition, true, 0, false);
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] In on_frame_recv_callback NGHTTP2_DATA QD_STREAM_FULLY_CLOSED, qdr_delivery_remote_state_updated(stream_data->out_dlv)", conn->conn_id, stream_data->stream_id);
- }
- }
- break;
- case NGHTTP2_HEADERS:
- case NGHTTP2_CONTINUATION: {
- if (!stream_data)
- return 0;
- if (frame->hd.type == NGHTTP2_CONTINUATION) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 CONTINUATION frame received", conn->conn_id, stream_id);
- }
- else {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 HEADERS frame received", conn->conn_id, stream_id);
- }
-
- if (frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) {
- /* All the headers have been received. Send out the AMQP message */
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 NGHTTP2_FLAG_END_HEADERS flag received, all headers have arrived", conn->conn_id, stream_id);
- stream_data->entire_header_arrived = true;
-
- if (stream_data->use_footer_properties) {
- qd_compose_end_map(stream_data->footer_properties);
- stream_data->entire_footer_arrived = true;
- qd_message_extend(stream_data->message, stream_data->footer_properties, 0);
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Closing footer map, extending message with footer", conn->conn_id, stream_id);
- }
- else {
- //
- // All header fields have been received. End the application properties map.
- //
- stream_data->use_footer_properties = true;
- qd_compose_end_map(stream_data->app_properties);
- }
-
- bool receive_complete = false;
- if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
- if (stream_data->entire_footer_arrived) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 NGHTTP2_FLAG_END_HEADERS and NGHTTP2_FLAG_END_STREAM flag received (footer), receive_complete=true", conn->conn_id, stream_id);
- }
- else {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 NGHTTP2_FLAG_END_HEADERS and NGHTTP2_FLAG_END_STREAM flag received, receive_complete=true", conn->conn_id, stream_id);
- }
- qd_message_set_receive_complete(stream_data->message);
- advance_stream_status(stream_data);
- receive_complete = true;
- }
-
- if (stream_data->entire_footer_arrived) {
- if (stream_data->in_dlv) {
- qdr_delivery_continue(http2_adaptor->core, stream_data->in_dlv, false);
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Entire footer arrived, qdr_delivery_continue "DLV_FMT, conn->conn_id, stream_id, DLV_ARGS(stream_data->in_dlv));
- }
- else {
- if (route_delivery(stream_data, receive_complete)) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Entire footer arrived, delivery routed successfully (on_frame_recv_callback)", conn->conn_id, stream_id);
- }
- }
- }
- else {
- //
- // All headers have arrived, send out the delivery with just the headers,
- // if/when the body arrives later, we will call the qdr_delivery_continue()
- //
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] All headers arrived, trying to route delivery (on_frame_recv_callback)", conn->conn_id, stream_id);
- if (route_delivery(stream_data, receive_complete)) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] All headers arrived, delivery routed successfully (on_frame_recv_callback)", conn->conn_id, stream_id);
- }
- }
-
- if (stream_data->out_dlv && !stream_data->disp_updated && !stream_data->out_dlv_decrefed && stream_data->status == QD_STREAM_FULLY_CLOSED) {
- qdr_delivery_remote_state_updated(http2_adaptor->core, stream_data->out_dlv, stream_data->out_dlv_local_disposition, true, 0, false);
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] In on_frame_recv_callback NGHTTP2_HEADERS QD_STREAM_FULLY_CLOSED, qdr_delivery_remote_state_updated(stream_data->out_dlv)", conn->conn_id, stream_data->stream_id);
- stream_data->disp_updated = true;
- }
- }
- }
- break;
- default:
- break;
- }
- return 0;
-}
-
-ssize_t read_data_callback(nghttp2_session *session,
- int32_t stream_id,
- uint8_t *buf,
- size_t length,
- uint32_t *data_flags,
- nghttp2_data_source *source,
- void *user_data)
-{
- qdr_http2_connection_t *conn = (qdr_http2_connection_t *)user_data;
- qdr_http2_stream_data_t *stream_data = (qdr_http2_stream_data_t *)source->ptr;
- qd_message_t *message = qdr_delivery_message(stream_data->out_dlv);
- qd_message_depth_status_t status = qd_message_check_depth(message, QD_DEPTH_BODY);
-
- // This flag tells nghttp2 that the data is not being copied into the buffer supplied by nghttp2 (uint8_t *buf).
- *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
-
- switch (status) {
- case QD_MESSAGE_DEPTH_OK: {
- //
- // At least one complete body performative has arrived. It is now safe to switch
- // over to the per-message extraction of body-data segments.
- //
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback QD_MESSAGE_DEPTH_OK", conn->conn_id, stream_data->stream_id);
-
-
- if (stream_data->next_stream_data) {
- stream_data->curr_stream_data = stream_data->next_stream_data;
- qd_iterator_free(stream_data->curr_stream_data_iter);
- stream_data->curr_stream_data_iter = qd_message_stream_data_iterator(stream_data->curr_stream_data);
- stream_data->curr_stream_data_result = stream_data->next_stream_data_result;
- stream_data->next_stream_data = 0;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback Use next_stream_data", conn->conn_id, stream_data->stream_id);
- }
-
- if (!stream_data->curr_stream_data) {
- stream_data->curr_stream_data_result = qd_message_next_stream_data(message, &stream_data->curr_stream_data);
- if (stream_data->curr_stream_data) {
- qd_iterator_free(stream_data->curr_stream_data_iter);
- stream_data->curr_stream_data_iter = qd_message_stream_data_iterator(stream_data->curr_stream_data);
- }
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback No body data, get qd_message_next_stream_data", conn->conn_id, stream_data->stream_id);
- }
-
- if (stream_data->next_stream_data == 0 && (stream_data->next_stream_data_result == QD_MESSAGE_STREAM_DATA_NO_MORE || stream_data->next_stream_data_result == QD_MESSAGE_STREAM_DATA_INVALID)) {
- stream_data->curr_stream_data_result = stream_data->next_stream_data_result;
- }
-
- switch (stream_data->curr_stream_data_result) {
- case QD_MESSAGE_STREAM_DATA_BODY_OK: {
- //
- // We have a new valid body-data segment. Handle it
- //
- size_t pn_buffs_write_capacity = pn_raw_connection_write_buffers_capacity(conn->pn_raw_conn);
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback QD_MESSAGE_STREAM_DATA_BODY_OK pn_raw_connection_write_buffers_capacity=%zu", conn->conn_id, stream_data->stream_id, pn_buffs_write_capacity);
-
- if (pn_buffs_write_capacity == 0) {
- //
- // Proton capacity is zero, we will come back later to write this stream, return for now.
- //
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Exiting read_data_callback, QD_MESSAGE_STREAM_DATA_BODY_OK pn_buffs_write_capacity=0, pausing stream, returning NGHTTP2_ERR_DEFERRED", conn->conn_id, stream_data->stream_id);
- stream_data->out_dlv_local_disposition = 0;
- return NGHTTP2_ERR_DEFERRED;
- }
-
- // total length of the payload (across all qd_buffers in the current body data)
- size_t payload_length = qd_message_stream_data_payload_length(stream_data->curr_stream_data);
-
- if (payload_length == 0) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback, payload_length=0", conn->conn_id, stream_data->stream_id);
-
- // The payload length is zero on this body data. Look ahead one body data to see if it is QD_MESSAGE_STREAM_DATA_NO_MORE
- stream_data->next_stream_data_result = qd_message_next_stream_data(message, &stream_data->next_stream_data);
- if (stream_data->next_stream_data_result == QD_MESSAGE_STREAM_DATA_NO_MORE) {
- if (!stream_data->out_msg_has_footer) {
- qd_message_stream_data_release(stream_data->curr_stream_data);
- qd_iterator_free(stream_data->curr_stream_data_iter);
- stream_data->curr_stream_data_iter = 0;
- stream_data->curr_stream_data = 0;
- }
-
- *data_flags |= NGHTTP2_DATA_FLAG_EOF;
- stream_data->out_msg_data_flag_eof = true;
- stream_data->out_msg_body_sent = true;
- stream_data->full_payload_handled = true;
- if (stream_data->next_stream_data) {
- qd_message_stream_data_release(stream_data->next_stream_data);
- stream_data->next_stream_data = 0;
- }
- stream_data->out_dlv_local_disposition = PN_ACCEPTED;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback, payload_length=0 and next_stream_data=QD_MESSAGE_STREAM_DATA_NO_MORE", conn->conn_id, stream_data->stream_id);
- }
- else if (stream_data->next_stream_data_result == QD_MESSAGE_STREAM_DATA_FOOTER_OK) {
- stream_data->full_payload_handled = true;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback, payload_length=0 and next_stream_data_result=QD_MESSAGE_STREAM_DATA_FOOTER_OK", conn->conn_id, stream_data->stream_id);
- }
- else {
- qd_message_stream_data_release(stream_data->curr_stream_data);
- qd_iterator_free(stream_data->curr_stream_data_iter);
- stream_data->curr_stream_data_iter = 0;
- stream_data->curr_stream_data = 0;
- }
-
- //
- // The payload length on this body data is zero. Nothing to do, just return zero to move on to the next body data. Usually, zero length body datas are a result of programmer error.
- //
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Exiting read_data_callback, payload_length=0, returning 0", conn->conn_id, stream_data->stream_id);
- return 0;
- }
-
- size_t bytes_to_send = 0;
- if (payload_length) {
- int remaining_payload_length = payload_length - stream_data->payload_handled;
-
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback remaining_payload_length=%i, length=%zu", conn->conn_id, stream_data->stream_id, remaining_payload_length, length);
-
- if (remaining_payload_length <= QD_HTTP2_BUFFER_SIZE) {
- if (length < remaining_payload_length) {
- bytes_to_send = length;
- stream_data->full_payload_handled = false;
- }
- else {
- bytes_to_send = remaining_payload_length;
- stream_data->full_payload_handled = true;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback remaining_payload_length (%i) <= QD_HTTP2_BUFFER_SIZE(16384), bytes_to_send=%zu", conn->conn_id, stream_data->stream_id, remaining_payload_length, bytes_to_send);
-
- // Look ahead one body data
- stream_data->next_stream_data_result = qd_message_next_stream_data(message, &stream_data->next_stream_data);
- if (stream_data->next_stream_data_result == QD_MESSAGE_STREAM_DATA_NO_MORE) {
- *data_flags |= NGHTTP2_DATA_FLAG_EOF;
- stream_data->out_msg_data_flag_eof = true;
- stream_data->out_msg_body_sent = true;
- stream_data->out_dlv_local_disposition = PN_ACCEPTED;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback, looking ahead one body data QD_MESSAGE_STREAM_DATA_NO_MORE", conn->conn_id, stream_data->stream_id);
- }
- else if (stream_data->next_stream_data_result == QD_MESSAGE_STREAM_DATA_FOOTER_OK) {
- stream_data->out_msg_has_footer = true;
- stream_data->out_msg_body_sent = true;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback, looking ahead one body data, QD_MESSAGE_STREAM_DATA_FOOTER_OK", conn->conn_id, stream_data->stream_id);
- }
- }
- }
- else {
- // This means that there is more that 16k worth of payload in one body data.
- // We want to send only 16k data per read_data_callback
- bytes_to_send = QD_HTTP2_BUFFER_SIZE;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback remaining_payload_length <= QD_HTTP2_BUFFER_SIZE ELSE bytes_to_send=%zu", conn->conn_id, stream_data->stream_id, bytes_to_send);
- stream_data->full_payload_handled = false;
- }
- }
-
- stream_data->bytes_out += bytes_to_send;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback returning bytes_to_send=%zu", conn->conn_id, stream_data->stream_id, bytes_to_send);
- return bytes_to_send;
- }
-
- case QD_MESSAGE_STREAM_DATA_FOOTER_OK:
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback QD_MESSAGE_STREAM_DATA_FOOTER_OK", conn->conn_id, stream_data->stream_id);
- if (!stream_data->out_msg_has_footer) {
- stream_data->out_msg_has_footer = true;
- stream_data->next_stream_data_result = qd_message_next_stream_data(message, &stream_data->next_stream_data);
- }
-
- if (stream_data->next_stream_data) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback QD_MESSAGE_STREAM_DATA_FOOTER_OK, we have a next_stream_data", conn->conn_id, stream_data->stream_id);
- }
- if (stream_data->next_stream_data_result == QD_MESSAGE_STREAM_DATA_INVALID) {
- stream_data->out_msg_has_footer = false;
- if (stream_data->next_stream_data) {
- qd_message_stream_data_release(stream_data->next_stream_data);
- stream_data->next_stream_data = 0;
- }
- }
- break;
-
- case QD_MESSAGE_STREAM_DATA_INCOMPLETE:
- //
- // A new segment has not completely arrived yet. Check again later.
- //
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback QD_MESSAGE_STREAM_DATA_INCOMPLETE, returning NGHTTP2_ERR_DEFERRED", conn->conn_id, stream_data->stream_id);
- stream_data->out_dlv_local_disposition = 0;
- return NGHTTP2_ERR_DEFERRED;
-
- case QD_MESSAGE_STREAM_DATA_NO_MORE: {
- //
- // We have already handled the last body-data segment for this delivery.
- //
- size_t pn_buffs_write_capacity = pn_raw_connection_write_buffers_capacity(conn->pn_raw_conn);
- if (pn_buffs_write_capacity == 0) {
- stream_data->out_dlv_local_disposition = 0;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback QD_MESSAGE_STREAM_DATA_NO_MORE - pn_buffs_write_capacity=0 send is not complete", conn->conn_id, stream_data->stream_id);
- return NGHTTP2_ERR_DEFERRED;
- }
- else {
- *data_flags |= NGHTTP2_DATA_FLAG_EOF;
- stream_data->out_msg_data_flag_eof = true;
- if (stream_data->out_msg_has_footer) {
- //
- // We have to send the trailer fields.
- // You cannot send trailer fields after sending frame with END_STREAM
- // set. To avoid this problem, one can set
- // NGHTTP2_DATA_FLAG_NO_END_STREAM along with
- // NGHTTP2_DATA_FLAG_EOF to signal the library not to set
- // END_STREAM in DATA frame.
- //
- *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback stream_data->out_msg_has_footer, setting NGHTTP2_DATA_FLAG_NO_END_STREAM", conn->conn_id, stream_data->stream_id);
- }
- stream_data->full_payload_handled = true;
- stream_data->out_msg_body_sent = true;
- stream_data->out_dlv_local_disposition = PN_ACCEPTED;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback QD_MESSAGE_STREAM_DATA_NO_MORE - stream_data->out_dlv_local_disposition = PN_ACCEPTED - send_complete=true, setting NGHTTP2_DATA_FLAG_EOF", conn->conn_id, stream_data->stream_id);
- }
-
- break;
- }
-
- case QD_MESSAGE_STREAM_DATA_INVALID:
- //
- // The body-data is corrupt in some way. Stop handling the delivery and reject it.
- //
- *data_flags |= NGHTTP2_DATA_FLAG_EOF;
- stream_data->out_msg_data_flag_eof = true;
- if (stream_data->curr_stream_data) {
- qd_message_stream_data_release(stream_data->curr_stream_data);
- qd_iterator_free(stream_data->curr_stream_data_iter);
- stream_data->curr_stream_data_iter = 0;
- stream_data->curr_stream_data = 0;
- }
- stream_data->out_dlv_local_disposition = PN_REJECTED;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_ERROR, "[C%"PRIu64"][S%"PRId32"] read_data_callback QD_MESSAGE_STREAM_DATA_INVALID", conn->conn_id, stream_data->stream_id);
- break;
- }
- break;
- }
-
- case QD_MESSAGE_DEPTH_INVALID:
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_ERROR, "[C%"PRIu64"][S%"PRId32"] read_data_callback QD_MESSAGE_DEPTH_INVALID", conn->conn_id, stream_data->stream_id);
- stream_data->out_dlv_local_disposition = PN_REJECTED;
- break;
-
- case QD_MESSAGE_DEPTH_INCOMPLETE:
- stream_data->out_dlv_local_disposition = 0;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] read_data_callback QD_MESSAGE_DEPTH_INCOMPLETE", conn->conn_id, stream_data->stream_id);
- return NGHTTP2_ERR_DEFERRED;
- }
-
- return 0;
-}
-
-
-
-qdr_http2_connection_t *qdr_http_connection_ingress(qd_http_listener_t* listener)
-{
- qdr_http2_connection_t* ingress_http_conn = new_qdr_http2_connection_t();
- ZERO(ingress_http_conn);
- ingress_http_conn->ingress = true;
- ingress_http_conn->context.context = ingress_http_conn;
- ingress_http_conn->context.handler = &handle_connection_event;
- ingress_http_conn->config = &(listener->config);
- ingress_http_conn->server = listener->server;
- ingress_http_conn->pn_raw_conn = pn_raw_connection();
- sys_atomic_init(&ingress_http_conn->raw_closed_read, 0);
- sys_atomic_init(&ingress_http_conn->raw_closed_write, 0);
- sys_atomic_init(&ingress_http_conn->q2_restart, 0);
- sys_atomic_init(&ingress_http_conn->delay_buffer_write, 0);
- DEQ_INIT(ingress_http_conn->buffs);
- DEQ_INIT(ingress_http_conn->streams);
- DEQ_INIT(ingress_http_conn->granted_read_buffs);
- ingress_http_conn->data_prd.read_callback = read_data_callback;
-
- sys_mutex_lock(http2_adaptor->lock);
- DEQ_INSERT_TAIL(http2_adaptor->connections, ingress_http_conn);
- sys_mutex_unlock(http2_adaptor->lock);
-
- nghttp2_session_server_new(&(ingress_http_conn->session), (nghttp2_session_callbacks*)http2_adaptor->callbacks, ingress_http_conn);
- pn_raw_connection_set_context(ingress_http_conn->pn_raw_conn, ingress_http_conn);
- pn_listener_raw_accept(listener->pn_listener, ingress_http_conn->pn_raw_conn);
- return ingress_http_conn;
-}
-
-static void grant_read_buffers(qdr_http2_connection_t *conn)
-{
- if (IS_ATOMIC_FLAG_SET(&conn->raw_closed_read))
- return;
-
- pn_raw_buffer_t raw_buffers[READ_BUFFERS];
- if (conn->pn_raw_conn) {
- size_t desired = pn_raw_connection_read_buffers_capacity(conn->pn_raw_conn);
- while (desired) {
- size_t i;
- for (i = 0; i < desired && i < READ_BUFFERS; ++i) {
- qd_http2_buffer_t *buf = qd_http2_buffer();
- DEQ_INSERT_TAIL(conn->granted_read_buffs, buf);
- raw_buffers[i].bytes = (char*) qd_http2_buffer_base(buf);
- raw_buffers[i].capacity = qd_http2_buffer_capacity(buf);
- raw_buffers[i].size = 0;
- raw_buffers[i].offset = 0;
- raw_buffers[i].context = (uintptr_t) buf;
- }
- desired -= i;
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"] Calling pn_raw_connection_give_read_buffers in grant_read_buffers", conn->conn_id);
- pn_raw_connection_give_read_buffers(conn->pn_raw_conn, raw_buffers, i);
- }
- }
-}
-
-
-static void qdr_http_detach(void *context, qdr_link_t *link, qdr_error_t *error, bool first, bool close)
-{
-}
-
-
-static void qdr_http_flow(void *context, qdr_link_t *link, int credit)
-{
- if (credit > 0) {
- qdr_http2_stream_data_t *stream_data = qdr_link_get_context(link);
- if (! stream_data)
- return;
- stream_data->in_link_credit += credit;
- if (!stream_data->in_dlv) {
- if (route_delivery(stream_data, qd_message_receive_complete(stream_data->message))) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] qdr_http_flow, delivery routed successfully", stream_data->conn->conn_id, stream_data->stream_id);
- }
- }
- }
-}
-
-
-static void qdr_http_offer(void *context, qdr_link_t *link, int delivery_count)
-{
-}
-
-
-static void qdr_http_drained(void *context, qdr_link_t *link)
-{
-}
-
-
-static void qdr_http_drain(void *context, qdr_link_t *link, bool mode)
-{
-}
-
-static int qdr_http_get_credit(void *context, qdr_link_t *link)
-{
- return 10;
-}
-
-
-ssize_t error_read_callback(nghttp2_session *session,
- int32_t stream_id,
- uint8_t *buf,
- size_t length,
- uint32_t *data_flags,
- nghttp2_data_source *source,
- void *user_data)
-{
- size_t len = 0;
- char *error_msg = (char *) source->ptr;
- if (error_msg) {
- len = strlen(error_msg);
- if (len > 0)
- memcpy(buf, error_msg, len);
- }
- *data_flags |= NGHTTP2_DATA_FLAG_EOF;
- return len;
-}
-
-static void qdr_http_delivery_update(void *context, qdr_delivery_t *dlv, uint64_t disp, bool settled)
-{
- qdr_http2_stream_data_t* stream_data = qdr_delivery_get_context(dlv);
- if (!stream_data)
- return;
-
- qdr_http2_connection_t *conn = stream_data->conn;
-
- //
- // DISPATCH-1849: In the case of large messages, the final DATA frame arriving from the server may or may not
- // contain the END_STREAM flag. In the cases when the final DATA frame does not contain the END_STREAM flag,
- // the router ends up forwarding all the data to the curl client without sending the END_STREAM to the client. The END_STREAM does arrive from the server
- // but not before the curl client closes the client connection after receiving all the data. The curl client
- // does not wait for the router to send an END_STREAM flag to close the connection. The client connection closure
- // triggers the link cleanup on the ingress connection, in turn freeing up all deliveries and its peer deliveries.
- // The peer delivery is released while it is still receiving the END_STREAM frame and the router crashes when we try to set receive complete
- // on the message because the message has already been freed. To solve this issue,
- // the stream_data->stream_force_closed flag is set to true when the peer delivery is released and this flag is
- // check when performing further actions on the delivery. No action on the peer delivery is performed
- // if this flag is set because the delivery and its underlying message have been freed.
- //
- if (settled && !conn->ingress && (disp == PN_RELEASED || disp == PN_MODIFIED || disp == PN_REJECTED)) {
- stream_data->stream_force_closed = true;
- }
-
- if (settled) {
- nghttp2_nv hdrs[3];
- if (conn->ingress && (disp == PN_RELEASED || disp == PN_MODIFIED || disp == PN_REJECTED)) {
- if (disp == PN_RELEASED || disp == PN_MODIFIED) {
- hdrs[0].name = (uint8_t *)":status";
- hdrs[0].value = (uint8_t *)"503";
- hdrs[0].namelen = 7;
- hdrs[0].valuelen = 3;
- hdrs[0].flags = NGHTTP2_NV_FLAG_NONE;
- }
- else if (disp == PN_REJECTED) {
- hdrs[0].name = (uint8_t *)":status";
- hdrs[0].value = (uint8_t *)"400";
- hdrs[0].namelen = 7;
- hdrs[0].valuelen = 3;
- hdrs[0].flags = NGHTTP2_NV_FLAG_NONE;
- }
-
- hdrs[1].name = (uint8_t *)"content-type";
- hdrs[1].value = (uint8_t *)"text/html; charset=utf-8";
- hdrs[1].namelen = 12;
- hdrs[1].valuelen = 24;
- hdrs[1].flags = NGHTTP2_NV_FLAG_NONE;
-
- hdrs[2].name = (uint8_t *)"content-length";
- hdrs[2].value = (uint8_t *)"0";
- hdrs[2].namelen = 14;
- hdrs[2].valuelen = 1;
- hdrs[2].flags = NGHTTP2_NV_FLAG_NONE;
-
- nghttp2_submit_headers(stream_data->conn->session, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_END_STREAM, stream_data->stream_id, NULL, hdrs, 3, 0);
- }
-
- if (!conn->ingress && (disp == PN_RELEASED || disp == PN_MODIFIED || disp == PN_REJECTED)) {
- //
- // On the server side connection, send a DATA frame with an END_STREAM flag thus closing the particular stream. We
- // don't want to close the entire connection like we did not the client side.
- //
- nghttp2_submit_data(conn->session, NGHTTP2_FLAG_END_STREAM, stream_data->stream_id, &conn->data_prd);
- }
-
- nghttp2_session_send(stream_data->conn->session);
-
- qdr_delivery_set_context(dlv, 0);
- if (stream_data->in_dlv == dlv) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] qdr_http_delivery_update, stream_data->in_dlv == dlv", stream_data->conn->conn_id, stream_data->stream_id);
- }
- else if (stream_data->out_dlv == dlv) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] qdr_http_delivery_update, stream_data->out_dlv == dlv", stream_data->conn->conn_id, stream_data->stream_id);
- }
-
- if (stream_data->status == QD_STREAM_FULLY_CLOSED) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] qdr_http_delivery_update, stream_data->status == QD_STREAM_FULLY_CLOSED", stream_data->conn->conn_id, stream_data->stream_id);
- }
- else {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] qdr_http_delivery_update, stream_data->status != QD_STREAM_FULLY_CLOSED", stream_data->conn->conn_id, stream_data->stream_id);
- }
-
- bool send_complete = stream_data->out_msg_send_complete;
- if (send_complete) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] qdr_http_delivery_update, send_complete=true", stream_data->conn->conn_id, stream_data->stream_id);
- }
- else {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] qdr_http_delivery_update, send_complete=false", stream_data->conn->conn_id, stream_data->stream_id);
- }
-
- qdr_delivery_decref(http2_adaptor->core, dlv, "HTTP2 adaptor - qdr_http_delivery_update");
- set_stream_data_delivery_flags(stream_data, dlv);
-
- if (send_complete && stream_data->status == QD_STREAM_FULLY_CLOSED) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] qdr_http_delivery_update, stream_data->status == QD_STREAM_FULLY_CLOSED, calling free_http2_stream_data, send_complete(dlv)=%i", stream_data->conn->conn_id, stream_data->stream_id, stream_data->out_msg_send_complete);
- free_http2_stream_data(stream_data, false);
- }
- else {
- stream_data->disp_applied = true;
- }
- }
-}
-
-
-static void qdr_http_conn_close(void *context, qdr_connection_t *qdr_conn, qdr_error_t *error)
-{
- if (qdr_conn) {
- qdr_http2_connection_t *http_conn = qdr_connection_get_context(qdr_conn);
- assert(http_conn);
- if (http_conn) {
- //
- // When the pn_raw_connection_close() is called, the
- // PN_RAW_CONNECTION_READ and PN_RAW_CONNECTION_WRITTEN events to be emitted so
- // the application can clean up buffers given to the raw connection. After that a
- // PN_RAW_CONNECTION_DISCONNECTED event will be emitted which will in turn call handle_disconnected().
- //
- http_conn->delete_egress_connections = true;
- pn_raw_connection_close(http_conn->pn_raw_conn);
- }
- }
-}
-
-
-static void qdr_http_conn_trace(void *context, qdr_connection_t *conn, bool trace)
-{
-}
-
-
-static void qdr_http_first_attach(void *context, qdr_connection_t *conn, qdr_link_t *link,
- qdr_terminus_t *source, qdr_terminus_t *target,
- qd_session_class_t session_class)
-{
-}
-
-
-static void qdr_copy_reply_to(qdr_http2_stream_data_t* stream_data, qd_iterator_t* reply_to)
-{
- int length = qd_iterator_length(reply_to);
- stream_data->reply_to = malloc(length + 1);
- qd_iterator_strncpy(reply_to, stream_data->reply_to, length + 1);
-}
-
-
-static void qdr_http_second_attach(void *context, qdr_link_t *link,
- qdr_terminus_t *source, qdr_terminus_t *target)
-{
- qdr_http2_stream_data_t *stream_data = (qdr_http2_stream_data_t*)qdr_link_get_context(link);
- if (stream_data) {
- if (qdr_link_direction(link) == QD_OUTGOING && source->dynamic) {
- if (stream_data->conn->ingress) {
- qdr_copy_reply_to(stream_data, qdr_terminus_get_address(source));
- if (route_delivery(stream_data, qd_message_receive_complete(stream_data->message))) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"] Reply-to available now, delivery routed successfully", stream_data->conn->conn_id);
- }
- else {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"] Reply-to available but delivery not routed (qdr_http_second_attach)", stream_data->conn->conn_id);
- }
- }
- qdr_link_flow(http2_adaptor->core, link, DEFAULT_CAPACITY, false);
- }
- }
-}
-
-static void qdr_http_activate(void *notused, qdr_connection_t *c)
-{
- sys_mutex_lock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
- qdr_http2_connection_t* conn = (qdr_http2_connection_t*) qdr_connection_get_context(c);
- if (conn) {
- if (conn->pn_raw_conn && !(IS_ATOMIC_FLAG_SET(&conn->raw_closed_read) && IS_ATOMIC_FLAG_SET(&conn->raw_closed_write))) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"] Activation triggered, calling pn_raw_connection_wake()", conn->conn_id);
- pn_raw_connection_wake(conn->pn_raw_conn);
- }
- else if (conn->activate_timer) {
- qd_timer_schedule(conn->activate_timer, 0);
- qd_log(http2_adaptor->log_source, QD_LOG_INFO, "[C%"PRIu64"] Activation triggered, no socket yet so scheduled timer", conn->conn_id);
- } else {
- qd_log(http2_adaptor->log_source, QD_LOG_ERROR, "[C%"PRIu64"] Cannot activate", conn->conn_id);
- }
- }
- sys_mutex_unlock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
-}
-
-static int qdr_http_push(void *context, qdr_link_t *link, int limit)
-{
- return qdr_link_process_deliveries(http2_adaptor->core, link, limit);
-}
-
-
-static void http_connector_establish(qdr_http2_connection_t *conn)
-{
- qd_log(http2_adaptor->log_source, QD_LOG_INFO, "[C%"PRIu64"] Connecting to: %s", conn->conn_id, conn->config->host_port);
- sys_mutex_lock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
- conn->pn_raw_conn = pn_raw_connection();
- pn_raw_connection_set_context(conn->pn_raw_conn, conn);
- pn_proactor_raw_connect(qd_server_proactor(conn->server), conn->pn_raw_conn, conn->config->host_port);
- sys_mutex_unlock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
-}
-
-
-/**
- * Converts the AMQP message into a HTTP request or response
- */
-uint64_t handle_outgoing_http(qdr_http2_stream_data_t *stream_data)
-{
- qdr_http2_connection_t *conn = stream_data->conn;
-
- if (IS_ATOMIC_FLAG_SET(&conn->raw_closed_write))
- return 0;
-
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] Starting to handle_outgoing_http", conn->conn_id);
- if (stream_data->out_dlv) {
-
- qd_message_t *message = qdr_delivery_message(stream_data->out_dlv);
-
- if (stream_data->out_msg_send_complete) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] handle_outgoing_http send is already complete, returning " DLV_FMT, conn->conn_id, stream_data->stream_id, DLV_ARGS(stream_data->out_dlv));
- return 0;
- }
-
- if (!stream_data->out_msg_header_sent) {
-
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] Header not sent yet", conn->conn_id);
-
- qd_iterator_t *group_id_itr = qd_message_field_iterator(message, QD_FIELD_GROUP_ID);
- stream_data->remote_site = (char*) qd_iterator_copy(group_id_itr);
- qd_iterator_free(group_id_itr);
-
-#ifndef NDEBUG
- qd_iterator_t *subject_itr = qd_message_field_iterator(message, QD_FIELD_SUBJECT);
- // Make sure there is a non-zero subject field iterator
- assert(subject_itr != 0);
- qd_iterator_free(subject_itr);
-#endif
- qd_iterator_t *app_properties_iter = qd_message_field_iterator(message, QD_FIELD_APPLICATION_PROPERTIES);
- qd_parsed_field_t *app_properties_fld = qd_parse(app_properties_iter);
-
- uint32_t count = qd_parse_sub_count(app_properties_fld);
-
- nghttp2_nv hdrs[count];
-
- for (uint32_t idx = 0; idx < count; idx++) {
- qd_parsed_field_t *key = qd_parse_sub_key(app_properties_fld, idx);
- qd_parsed_field_t *val = qd_parse_sub_value(app_properties_fld, idx);
- qd_iterator_t *key_raw = qd_parse_raw(key);
- qd_iterator_t *val_raw = qd_parse_raw(val);
-
- hdrs[idx].name = (uint8_t *)qd_iterator_copy(key_raw);
- hdrs[idx].value = (uint8_t *)qd_iterator_copy(val_raw);
- hdrs[idx].namelen = qd_iterator_length(key_raw);
- hdrs[idx].valuelen = qd_iterator_length(val_raw);
- hdrs[idx].flags = NGHTTP2_NV_FLAG_NONE;
-
- if (strcmp(METHOD, (const char *)hdrs[idx].name) == 0) {
- stream_data->method = qd_strdup((const char *)hdrs[idx].value);
- }
- if (strcmp(STATUS, (const char *)hdrs[idx].name) == 0) {
- stream_data->request_status = qd_strdup((const char *)hdrs[idx].value);
- }
- }
-
- int stream_id = stream_data->conn->ingress?stream_data->stream_id: -1;
-
- create_settings_frame(conn);
-
- uint8_t flags = 0;
- stream_data->curr_stream_data_result = qd_message_next_stream_data(message, &stream_data->curr_stream_data);
- if (stream_data->curr_stream_data_result == QD_MESSAGE_STREAM_DATA_BODY_OK) {
- size_t payload_length = qd_message_stream_data_payload_length(stream_data->curr_stream_data);
-
- if (payload_length == 0) {
- stream_data->next_stream_data_result = qd_message_next_stream_data(message, &stream_data->next_stream_data);
-
- if (stream_data->next_stream_data_result == QD_MESSAGE_STREAM_DATA_NO_MORE) {
- if (stream_data->next_stream_data) {
- qd_message_stream_data_release(stream_data->next_stream_data);
- stream_data->next_stream_data = 0;
- }
-
- qd_message_stream_data_release(stream_data->curr_stream_data);
-
- stream_data->curr_stream_data = 0;
- flags = NGHTTP2_FLAG_END_STREAM;
- stream_data->out_msg_has_body = false;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] Message has no body, sending NGHTTP2_FLAG_END_STREAM with nghttp2_submit_headers", conn->conn_id);
- }
- }
- else {
- stream_data->curr_stream_data_iter = qd_message_stream_data_iterator(stream_data->curr_stream_data);
- }
- }
-
- // There is a body for this message, set the delay_buffer_write so the buffers are not immediately
- // pushed out on header submission.
- if (stream_data->out_msg_has_body) {
- SET_ATOMIC_FLAG(&conn->delay_buffer_write);
- }
-
- stream_data->stream_id = nghttp2_submit_headers(conn->session,
- flags,
- stream_id,
- NULL,
- hdrs,
- count,
- stream_data);
-
- if (stream_id != -1) {
- stream_data->stream_id = stream_id;
- }
-
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] handle_outgoing_http, out_dlv before sending Outgoing headers "DLV_FMT, conn->conn_id, stream_data->stream_id, DLV_ARGS(stream_data->out_dlv));
-
- for (uint32_t idx = 0; idx < count; idx++) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 HEADER Outgoing [%s=%s]", conn->conn_id, stream_data->stream_id, (char *)hdrs[idx].name, (char *)hdrs[idx].value);
- }
-
- nghttp2_session_send(conn->session);
- conn->client_magic_sent = true;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Headers submitted", conn->conn_id, stream_data->stream_id);
-
- qd_iterator_free(app_properties_iter);
- qd_parse_free(app_properties_fld);
-
- for (uint32_t idx = 0; idx < count; idx++) {
- free(hdrs[idx].name);
- free(hdrs[idx].value);
- }
-
- }
- else {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Headers already submitted, Proceeding with the body", conn->conn_id, stream_data->stream_id);
- }
-
- if (stream_data->out_msg_has_body) {
- if (stream_data->out_msg_header_sent) {
- // This is usually called if there are many AMQP data streams objects in a delivery. These data streams were created on the inbound AMQP side using the qdr_delivery_continue() function.
- nghttp2_session_resume_data(conn->session, stream_data->stream_id);
- nghttp2_session_send(conn->session);
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] nghttp2_session_send - write_buffers done for resumed stream", conn->conn_id, stream_data->stream_id);
- }
- else {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Processing message body", conn->conn_id, stream_data->stream_id);
- conn->data_prd.source.ptr = stream_data;
-
- int rv = 0;
- rv = nghttp2_submit_data(conn->session, NGHTTP2_FLAG_END_STREAM, stream_data->stream_id, &conn->data_prd);
- if (rv != 0) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_ERROR, "[C%"PRIu64"][S%"PRId32"] Error submitting data rv=%i", conn->conn_id, stream_data->stream_id, rv);
- }
- else {
- if (conn->session) {
- nghttp2_session_send(conn->session);
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] nghttp2_session_send - done", conn->conn_id, stream_data->stream_id);
- }
- }
- }
- }
- else {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Message has no body", conn->conn_id, stream_data->stream_id);
- }
- stream_data->out_msg_header_sent = true;
-
- CLEAR_ATOMIC_FLAG(&conn->delay_buffer_write);
-
- if (stream_data->out_msg_has_footer) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Message has a footer", conn->conn_id, stream_data->stream_id);
- bool send_footer = false;
- if (stream_data->out_msg_has_body && !stream_data->out_msg_body_sent) {
- if (stream_data->out_msg_body_sent) {
- send_footer = true;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] send_footer = true", conn->conn_id, stream_data->stream_id);
- }
- else {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] send_footer = false", conn->conn_id, stream_data->stream_id);
- }
- }
- else {
- send_footer = true;
- }
-
- //
- // We have a footer and are ready to send it.
- //
- if (send_footer) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Starting to send footer", conn->conn_id, stream_data->stream_id);
- // Send the properties in the footer as a HEADERS frame.
- qd_iterator_t *footer_properties_iter = stream_data->footer_stream_data_iter;
- qd_parsed_field_t *footer_properties_fld = qd_parse(footer_properties_iter);
-
- uint32_t count = qd_parse_sub_count(footer_properties_fld);
-
- nghttp2_nv hdrs[count];
-
- for (uint32_t idx = 0; idx < count; idx++) {
- qd_parsed_field_t *key = qd_parse_sub_key(footer_properties_fld, idx);
- qd_parsed_field_t *val = qd_parse_sub_value(footer_properties_fld, idx);
- qd_iterator_t *key_raw = qd_parse_raw(key);
- qd_iterator_t *val_raw = qd_parse_raw(val);
-
- hdrs[idx].name = (uint8_t *)qd_iterator_copy(key_raw);
- hdrs[idx].value = (uint8_t *)qd_iterator_copy(val_raw);
- hdrs[idx].namelen = qd_iterator_length(key_raw);
- hdrs[idx].valuelen = qd_iterator_length(val_raw);
- hdrs[idx].flags = NGHTTP2_NV_FLAG_NONE;
- }
-
- nghttp2_submit_headers(conn->session,
- NGHTTP2_FLAG_END_STREAM,
- stream_data->stream_id,
- NULL,
- hdrs,
- count,
- stream_data);
-
- for (uint32_t idx = 0; idx < count; idx++) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] HTTP2 HEADER(footer) Outgoing [%s=%s]", conn->conn_id, stream_data->stream_id, (char *)hdrs[idx].name, (char *)hdrs[idx].value);
- }
-
- nghttp2_session_send(conn->session);
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Headers(from footer) submitted", conn->conn_id, stream_data->stream_id);
-
-
- qd_iterator_free(footer_properties_iter);
- qd_parse_free(footer_properties_fld);
- if (stream_data->footer_stream_data) {
- qd_message_stream_data_release(stream_data->footer_stream_data);
- }
- if (stream_data->curr_stream_data) {
- qd_message_stream_data_release(stream_data->curr_stream_data);
- qd_iterator_free(stream_data->curr_stream_data_iter);
- stream_data->curr_stream_data_iter = 0;
- stream_data->curr_stream_data = 0;
- }
- if (stream_data->next_stream_data) {
- qd_message_stream_data_release(stream_data->next_stream_data);
- stream_data->next_stream_data = 0;
- }
-
- for (uint32_t idx = 0; idx < count; idx++) {
- free(hdrs[idx].name);
- free(hdrs[idx].value);
- }
- }
- }
- else {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Message has no footer", conn->conn_id, stream_data->stream_id);
- }
-
- if (stream_data->out_msg_header_sent) {
- if (stream_data->out_msg_has_body) {
- if (stream_data->out_msg_body_sent) {
- qd_message_set_send_complete(qdr_delivery_message(stream_data->out_dlv));
- stream_data->out_msg_send_complete = true;
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] handle_outgoing_http, out_dlv send_complete "DLV_FMT , conn->conn_id, stream_data->stream_id, DLV_ARGS(stream_data->out_dlv));
-
- }
- }
- else {
- qd_message_set_send_complete(qdr_delivery_message(stream_data->out_dlv));
- stream_data->out_msg_send_complete = true;
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] handle_outgoing_http, out_dlv send_complete "DLV_FMT, conn->conn_id, stream_data->stream_id, DLV_ARGS(stream_data->out_dlv));
- }
- }
-
- if (qd_message_send_complete(qdr_delivery_message(stream_data->out_dlv))) {
- advance_stream_status(stream_data);
- if (!stream_data->disp_updated && stream_data->status == QD_STREAM_FULLY_CLOSED) {
- qdr_delivery_remote_state_updated(http2_adaptor->core, stream_data->out_dlv, stream_data->out_dlv_local_disposition, true, 0, false);
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] In handle_outgoing_http, qdr_delivery_remote_state_updated(stream_data->out_dlv)", conn->conn_id, stream_data->stream_id);
- stream_data->disp_updated = true;
- qdr_delivery_decref(http2_adaptor->core, stream_data->out_dlv, "HTTP2 adaptor out_dlv - handle_outgoing_http");
- set_stream_data_delivery_flags(stream_data, stream_data->out_dlv);
- }
- }
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] Finished handle_outgoing_http", conn->conn_id);
- }
- else {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] No out_dlv, no handle_outgoing_http", conn->conn_id);
- }
- return 0;
-}
-
-static uint64_t qdr_http_deliver(void *context, qdr_link_t *link, qdr_delivery_t *delivery, bool settled)
-{
- qdr_http2_stream_data_t *stream_data = (qdr_http2_stream_data_t*)qdr_link_get_context(link);
-
- if (!stream_data)
- return 0;
-
- qdr_http2_connection_t *conn = stream_data->conn;
-
- if (link == stream_data->conn->stream_dispatcher) {
- //
- // Let's make an outbound connection to the configured connector.
- //
- qdr_http2_connection_t *conn = stream_data->conn;
-
- qdr_http2_stream_data_t *stream_data = create_http2_stream_data(conn, 0);
- if (!stream_data->out_dlv) {
- stream_data->out_dlv = delivery;
- qdr_delivery_incref(delivery, "egress out_dlv referenced by HTTP2 adaptor");
- }
- qdr_terminus_t *source = qdr_terminus(0);
- qdr_terminus_set_address(source, conn->config->address);
-
- // Receiving link.
- stream_data->out_link = qdr_link_first_attach(conn->qdr_conn,
- QD_OUTGOING,
- source, // qdr_terminus_t *source,
- qdr_terminus(0), // qdr_terminus_t *target,
- "http.egress.out", // const char *name,
- 0, // const char *terminus_addr,
- true,
- delivery,
- &(stream_data->outgoing_id));
- qdr_link_set_context(stream_data->out_link, stream_data);
- qd_iterator_t *fld_iter = qd_message_field_iterator(qdr_delivery_message(delivery), QD_FIELD_REPLY_TO);
- stream_data->reply_to = (char *)qd_iterator_copy(fld_iter);
- qd_iterator_free(fld_iter);
-
- // Sender link.
- qdr_terminus_t *target = qdr_terminus(0);
- qdr_terminus_set_address(target, stream_data->reply_to);
- stream_data->in_link = qdr_link_first_attach(conn->qdr_conn,
- QD_INCOMING,
- qdr_terminus(0), //qdr_terminus_t *source,
- target, //qdr_terminus_t *target,
- "http.egress.in", //const char *name,
- 0, //const char *terminus_addr,
- false,
- 0,
- &(stream_data->incoming_id));
- qdr_link_set_context(stream_data->in_link, stream_data);
- return QD_DELIVERY_MOVED_TO_NEW_LINK;
- }
-
- if (conn->ingress) {
- if (!stream_data->out_dlv) {
- stream_data->out_dlv = delivery;
- qdr_delivery_incref(delivery, "ingress out_dlv referenced by HTTP2 adaptor");
- }
- }
- qd_log(http2_adaptor->log_source, QD_LOG_DEBUG, "[C%"PRIu64"][S%"PRId32"] qdr_http_deliver - call handle_outgoing_http", conn->conn_id, stream_data->stream_id);
- uint64_t disp = handle_outgoing_http(stream_data);
- if (stream_data->status == QD_STREAM_FULLY_CLOSED && disp == PN_ACCEPTED) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] qdr_http_deliver - calling free_http2_stream_data", conn->conn_id, stream_data->stream_id);
- free_http2_stream_data(stream_data, false);
- }
- return disp;
-}
-
-
-/**
- * Takes read buffers from proton raw connection and feeds the binary http2 frame data
- * to nghttp2 via the nghttp2_session_mem_recv() function.
- * All pertinent nghttp2 callbacks are called before the call to nghttp2_session_mem_recv() completes.
- */
-static int handle_incoming_http(qdr_http2_connection_t *conn)
-{
- //
- // This fix is a for nodejs server (router acting as client).
- // This is what happens -
- // 1. nodejs sends a SETTINGS frame immediately after we open the connection. (this is legal)
- // 2. Router sends -
- // 2a. Client magic
- // 2b. SETTINGS frame with ack=true (here the router is responding to the SETTINGS frame from nodejs in step 1)
- // 2c. SETTINGS frame ack=false(this is the router's inital settings frame)
- // 2d. GET request
- // 3. Nodejs responds with GOAWAY. Not sure why
- // To remedy this problem, when nodejs sends the initial SETTINGS frame, we don't tell nghttp2 about it. So step 2c happens before step 2b and nodejs is now happy
- //
- if (!conn->ingress) {
- if (!conn->client_magic_sent) {
- return 0;
- }
-
- }
-
- pn_raw_buffer_t raw_buffers[READ_BUFFERS];
- size_t n;
- int count = 0;
- int rv = 0;
-
- if (!conn->pn_raw_conn)
- return 0;
-
- bool close_conn = false;
-
- while ( (n = pn_raw_connection_take_read_buffers(conn->pn_raw_conn, raw_buffers, READ_BUFFERS)) ) {
- for (size_t i = 0; i < n && raw_buffers[i].bytes; ++i) {
- qd_http2_buffer_t *buf = (qd_http2_buffer_t*) raw_buffers[i].context;
- DEQ_REMOVE(conn->granted_read_buffs, buf);
- uint32_t raw_buff_size = raw_buffers[i].size;
- qd_http2_buffer_insert(buf, raw_buff_size);
- count += raw_buff_size;
-
- if (raw_buff_size > 0 && !close_conn) {
- qd_log(http2_adaptor->log_source, QD_LOG_DEBUG, "[C%"PRIu64"] handle_incoming_http - Calling nghttp2_session_mem_recv qd_http2_buffer of size %"PRIu32" ", conn->conn_id, raw_buff_size);
- rv = nghttp2_session_mem_recv(conn->session, qd_http2_buffer_base(buf), qd_http2_buffer_size(buf));
- if (rv < 0) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_ERROR, "[C%"PRIu64"] Error in nghttp2_session_mem_recv rv=%i", conn->conn_id, rv);
- if (rv == NGHTTP2_ERR_FLOODED) {
- // Flooding was detected in this HTTP/2 session, and it must be closed. This is most likely caused by misbehavior of peer.
- // If the client magic is bad, we need to close the connection.
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_ERROR, "[C%"PRIu64"] HTTP NGHTTP2_ERR_FLOODED", conn->conn_id);
- nghttp2_submit_goaway(conn->session, 0, 0, NGHTTP2_PROTOCOL_ERROR, (uint8_t *)"Protocol Error", 14);
- }
- else if (rv == NGHTTP2_ERR_CALLBACK_FAILURE) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_ERROR, "[C%"PRIu64"] HTTP NGHTTP2_ERR_CALLBACK_FAILURE", conn->conn_id);
- nghttp2_submit_goaway(conn->session, 0, 0, NGHTTP2_PROTOCOL_ERROR, (uint8_t *)"Internal Error", 14);
- }
- else if (rv == NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_ERROR, "[C%"PRIu64"] HTTP2 Protocol error, NGHTTP2_ERR_BAD_CLIENT_MAGIC, closing connection", conn->conn_id);
- nghttp2_submit_goaway(conn->session, 0, 0, NGHTTP2_PROTOCOL_ERROR, (uint8_t *)"Bad Client Magic", 16);
- }
- else {
- nghttp2_submit_goaway(conn->session, 0, 0, NGHTTP2_PROTOCOL_ERROR, (uint8_t *)"Protocol Error", 14);
- }
- nghttp2_session_send(conn->session);
-
- //
- // An error was received from nghttp2, the connection needs to be closed.
- //
- close_conn = true;
- }
- }
- free_qd_http2_buffer_t(buf);
- }
- }
-
- if (close_conn) {
- pn_raw_connection_close(conn->pn_raw_conn);
- }
- else {
- grant_read_buffers(conn);
- }
- nghttp2_session_send(conn->session);
-
- return count;
-}
-
-
-qdr_http2_connection_t *qdr_http_connection_ingress_accept(qdr_http2_connection_t* ingress_http_conn)
-{
- ingress_http_conn->remote_address = get_address_string(ingress_http_conn->pn_raw_conn);
- qdr_connection_info_t *info = qdr_connection_info(false, //bool is_encrypted,
- false, //bool is_authenticated,
- true, //bool opened,
- "", //char *sasl_mechanisms,
- QD_INCOMING, //qd_direction_t dir,
- ingress_http_conn->remote_address, //const char *host,
- "", //const char *ssl_proto,
- "", //const char *ssl_cipher,
- "", //const char *user,
- "HttpAdaptor", //const char *container,
- 0, //pn_data_t *connection_properties,
- 0, //int ssl_ssf,
- false, //bool ssl,
- "", // peer router version,
- false); // streaming links
-
- qdr_connection_t *conn = qdr_connection_opened(http2_adaptor->core,
- http2_adaptor->adaptor,
- true,
- QDR_ROLE_NORMAL,
- 1,
- qd_server_allocate_connection_id(ingress_http_conn->server),
- 0,
- 0,
- false,
- false,
- 250,
- 0,
- 0,
- info,
- 0,
- 0);
-
- ingress_http_conn->qdr_conn = conn;
- ingress_http_conn->conn_id = conn->identity;
- qdr_connection_set_context(conn, ingress_http_conn);
- ingress_http_conn->connection_established = true;
- return ingress_http_conn;
-}
-
-
-static void restart_streams(qdr_http2_connection_t *http_conn)
-{
- qdr_http2_stream_data_t *stream_data = DEQ_HEAD(http_conn->streams);
- if (!stream_data) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"] In restart_streams, no stream_data, returning", http_conn->conn_id);
- return;
- }
-
- DEQ_REMOVE_HEAD(http_conn->streams);
- DEQ_INSERT_TAIL(http_conn->streams, stream_data);
- stream_data = DEQ_HEAD(http_conn->streams);
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] In restart_streams swapped head and tail streams", http_conn->conn_id, stream_data->stream_id);
- while (stream_data) {
- if (stream_data->status == QD_STREAM_FULLY_CLOSED) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] In restart_streams QD_STREAM_FULLY_CLOSED, not restarting stream", http_conn->conn_id, stream_data->stream_id);
-
- if (stream_data->out_dlv && !stream_data->disp_updated && !stream_data->out_dlv_decrefed && stream_data->status == QD_STREAM_FULLY_CLOSED ) {
- // A call to qdr_delivery_remote_state_updated will free the out_dlv
- qdr_delivery_remote_state_updated(http2_adaptor->core, stream_data->out_dlv, stream_data->out_dlv_local_disposition, true, 0, false);
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] In restart_streams QD_STREAM_FULLY_CLOSED, qdr_delivery_remote_state_updated(stream_data->out_dlv)", http_conn->conn_id, stream_data->stream_id);
- stream_data->disp_updated = true;
- }
- qdr_http2_stream_data_t *next_stream_data = 0;
- next_stream_data = DEQ_NEXT(stream_data);
- if(stream_data->out_msg_send_complete && stream_data->disp_applied) {
- free_http2_stream_data(stream_data, false);
- }
- stream_data = next_stream_data;
- }
- else {
- if (stream_data->out_dlv_local_disposition != PN_ACCEPTED) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"][S%"PRId32"] Restarting stream in restart_streams()", http_conn->conn_id, stream_data->stream_id);
- handle_outgoing_http(stream_data);
- }
- stream_data = DEQ_NEXT(stream_data);
- }
- }
-}
-
-
-static void qdr_del_http2_connection_CT(qdr_core_t *core, qdr_action_t *action, bool discard)
-{
- //
- // DISPATCH-1996: discard is true in the case where this action is called from qdr_core_free()
- // This means that the qdr_adaptors_finalize has already been called and the connection in question has already been freed.
- // No need to do anything now, if discard, just return.
- //
- if (discard)
- return;
-
- qdr_http2_connection_t *conn = (qdr_http2_connection_t*) action->args.general.context_1;
- free_qdr_http2_connection(conn, false);
-}
-
-
-static void close_connections(qdr_http2_connection_t* conn)
-{
- qdr_connection_set_context(conn->qdr_conn, 0);
- qdr_connection_closed(conn->qdr_conn);
- conn->qdr_conn = 0;
- qdr_action_t *action = qdr_action(qdr_del_http2_connection_CT, "delete_http2_connection");
- action->args.general.context_1 = conn;
- qdr_action_enqueue(http2_adaptor->core, action);
-}
-
-static void clean_conn_buffs(qdr_http2_connection_t* conn)
-{
- //
- // Free all the buffers on this session. This session is closed and any unsent buffers should be freed.
- //
- qd_http2_buffer_t *buf = DEQ_HEAD(conn->buffs);
- qd_http2_buffer_t *curr_buf = 0;
- while (buf) {
- curr_buf = buf;
- DEQ_REMOVE_HEAD(conn->buffs);
- buf = DEQ_HEAD(conn->buffs);
- free_qd_http2_buffer_t(curr_buf);
- }
-}
-
-static void clean_conn(qdr_http2_connection_t* conn)
-{
- free_all_connection_streams(conn, false);
-
- //
- // This closes the nghttp2 session. Next time when a new connection is opened, a new nghttp2 session
- // will be created by calling nghttp2_session_client_new
- //
- nghttp2_session_del(conn->session);
- conn->session = 0;
- clean_conn_buffs(conn);
-}
-
-
-static void handle_disconnected(qdr_http2_connection_t* conn)
-{
- sys_mutex_lock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
-
- if (conn->pn_raw_conn) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"] Setting conn->pn_raw_conn=0", conn->conn_id);
- pn_raw_connection_set_context(conn->pn_raw_conn, 0);
- conn->pn_raw_conn = 0;
- }
-
- if (conn->ingress) {
- clean_conn(conn);
- close_connections(conn);
- }
- else {
- if (conn->stream_dispatcher) {
- qdr_http2_stream_data_t *stream_data = qdr_link_get_context(conn->stream_dispatcher);
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"] Detaching stream dispatcher link on egress connection, freed associated stream data", conn->conn_id);
- qdr_link_detach(conn->stream_dispatcher, QD_CLOSED, 0);
- qdr_link_set_context(conn->stream_dispatcher, 0);
- conn->stream_dispatcher = 0;
- if (stream_data) {
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] Freeing stream_data (stream_dispatcher, handle_disconnected) (%lx)", conn->conn_id, (long) stream_data);
- free_qdr_http2_stream_data_t(stream_data);
- }
- conn->stream_dispatcher_stream_data = 0;
-
- }
- if (conn->delete_egress_connections) {
- // The config has already been freed by the qd_http_connector_decref() function, set it to zero here
- conn->config = 0;
- // It is important that clean_conn be called *after* the conn->config has been set to zero
- clean_conn(conn);
- close_connections(conn);
- }
- else {
- clean_conn(conn);
- }
- }
- sys_mutex_unlock(qd_server_get_activation_lock(http2_adaptor->core->qd->server));
-}
-
-
-static void egress_conn_timer_handler(void *context)
-{
- qdr_http2_connection_t* conn = (qdr_http2_connection_t*) context;
-
- if (conn->pn_raw_conn || conn->connection_established)
- return;
-
- qd_log(http2_adaptor->log_source, QD_LOG_INFO, "[C%"PRIu64"] Running egress_conn_timer_handler", conn->conn_id);
-
- if (!conn->ingress) {
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "[C%"PRIu64"] - Egress_conn_timer_handler - Trying to establishing outbound connection", conn->conn_id);
- http_connector_establish(conn);
- }
-}
-
-
-static void create_stream_dispatcher_link(qdr_http2_connection_t *egress_http_conn)
-{
- if (egress_http_conn->stream_dispatcher)
- return;
-
- qdr_terminus_t *source = qdr_terminus(0);
- qdr_terminus_set_address(source, egress_http_conn->config->address);
- egress_http_conn->stream_dispatcher = qdr_link_first_attach(egress_http_conn->qdr_conn,
- QD_OUTGOING,
- source, //qdr_terminus_t *source,
- qdr_terminus(0), //qdr_terminus_t *target,
- "stream_dispatcher", //const char *name,
- 0, //const char *terminus_addr,
- false,
- 0,
- &(egress_http_conn->stream_dispatcher_id));
-
- // Create a dummy stream_data object and set that as context.
- qdr_http2_stream_data_t *stream_data = new_qdr_http2_stream_data_t();
-
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] Created new stream_data for stream_dispatcher (%lx)", egress_http_conn->conn_id, (long) stream_data);
-
- ZERO(stream_data);
- stream_data->conn = egress_http_conn;
- qdr_link_set_context(egress_http_conn->stream_dispatcher, stream_data);
-
- // This is added specifically to deal with the shutdown leak of the dispatcher stream data.
- // The core frees all links before it calls adaptor final. so we cannot get the stream data from the qdr_link context.
- egress_http_conn->stream_dispatcher_stream_data = stream_data;
-}
-
-
-qdr_http2_connection_t *qdr_http_connection_egress(qd_http_connector_t *connector)
-{
- qdr_http2_connection_t* egress_http_conn = new_qdr_http2_connection_t();
- ZERO(egress_http_conn);
- egress_http_conn->activate_timer = qd_timer(http2_adaptor->core->qd, egress_conn_timer_handler, egress_http_conn);
-
- egress_http_conn->ingress = false;
- egress_http_conn->context.context = egress_http_conn;
- egress_http_conn->context.handler = &handle_connection_event;
- egress_http_conn->config = &(connector->config);
- egress_http_conn->server = connector->server;
- egress_http_conn->data_prd.read_callback = read_data_callback;
-
- DEQ_INIT(egress_http_conn->buffs);
- DEQ_INIT(egress_http_conn->streams);
- DEQ_INIT(egress_http_conn->granted_read_buffs);
- sys_atomic_init(&egress_http_conn->raw_closed_read, 0);
- sys_atomic_init(&egress_http_conn->raw_closed_write, 0);
- sys_atomic_init(&egress_http_conn->delay_buffer_write, 0);
- sys_atomic_init(&egress_http_conn->q2_restart, 0);
-
- sys_mutex_lock(http2_adaptor->lock);
- DEQ_INSERT_TAIL(http2_adaptor->connections, egress_http_conn);
- sys_mutex_unlock(http2_adaptor->lock);
-
- qdr_connection_info_t *info = qdr_connection_info(false, //bool is_encrypted,
- false, //bool is_authenticated,
- true, //bool opened,
- "", //char *sasl_mechanisms,
- QD_OUTGOING, //qd_direction_t dir,
- egress_http_conn->config->host_port, //const char *host,
- "", //const char *ssl_proto,
- "", //const char *ssl_cipher,
- "", //const char *user,
- "httpAdaptor", //const char *container,
- 0, //pn_data_t *connection_properties,
- 0, //int ssl_ssf,
- false, //bool ssl,
- "", // peer router version,
- false); // streaming links
-
- qdr_connection_t *conn = qdr_connection_opened(http2_adaptor->core,
- http2_adaptor->adaptor,
- true,
- QDR_ROLE_NORMAL,
- 1,
- qd_server_allocate_connection_id(egress_http_conn->server),
- 0,
- 0,
- false,
- false,
- 250,
- 0,
- 0,
- info,
- 0,
- 0);
- egress_http_conn->qdr_conn = conn;
- connector->ctx = conn;
-
- egress_http_conn->conn_id = conn->identity;
- qdr_connection_set_context(conn, egress_http_conn);
- create_stream_dispatcher_link(egress_http_conn);
- return egress_http_conn;
-}
-
-static void handle_connection_event(pn_event_t *e, qd_server_t *qd_server, void *context)
-{
- qdr_http2_connection_t *conn = (qdr_http2_connection_t*) context;
- qd_log_source_t *log = http2_adaptor->log_source;
- switch (pn_event_type(e)) {
- case PN_RAW_CONNECTION_CONNECTED: {
- conn->goaway_received = false;
- if (conn->ingress) {
- qdr_http_connection_ingress_accept(conn);
- send_settings_frame(conn);
- qd_log(log, QD_LOG_INFO, "[C%"PRIu64"] Accepted Ingress ((PN_RAW_CONNECTION_CONNECTED)) from %s", conn->conn_id, conn->remote_address);
- } else {
- CLEAR_ATOMIC_FLAG(&conn->raw_closed_read);
- CLEAR_ATOMIC_FLAG(&conn->raw_closed_write);
- if (!conn->session) {
- nghttp2_session_client_new(&conn->session, (nghttp2_session_callbacks *)http2_adaptor->callbacks, (void *)conn);
- send_settings_frame(conn);
- conn->client_magic_sent = true;
- }
- qd_log(log, QD_LOG_INFO, "[C%"PRIu64"] Connected Egress (PN_RAW_CONNECTION_CONNECTED)", conn->conn_id);
- conn->connection_established = true;
- create_stream_dispatcher_link(conn);
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] Created stream_dispatcher_link in PN_RAW_CONNECTION_CONNECTED", conn->conn_id);
- while (qdr_connection_process(conn->qdr_conn)) {}
- }
- break;
- }
- case PN_RAW_CONNECTION_CLOSED_READ: {
- if (conn->q2_blocked) {
- conn->q2_blocked = false;
- }
- SET_ATOMIC_FLAG(&conn->raw_closed_read);
- if (conn->pn_raw_conn)
- pn_raw_connection_close(conn->pn_raw_conn);
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] PN_RAW_CONNECTION_CLOSED_READ", conn->conn_id);
- break;
- }
- case PN_RAW_CONNECTION_CLOSED_WRITE: {
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] PN_RAW_CONNECTION_CLOSED_WRITE", conn->conn_id);
- SET_ATOMIC_FLAG(&conn->raw_closed_write);
- break;
- }
- case PN_RAW_CONNECTION_DISCONNECTED: {
- if (conn->ingress) {
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] Ingress PN_RAW_CONNECTION_DISCONNECTED", conn->conn_id);
- }
- else {
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] Egress PN_RAW_CONNECTION_DISCONNECTED", conn->conn_id);
- conn->client_magic_sent = false;
- if (!conn->delete_egress_connections) {
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] Scheduling 2 second timer to reconnect to egress connection", conn->conn_id);
- qd_timer_schedule(conn->activate_timer, 2000);
- }
- }
- conn->connection_established = false;
- handle_disconnected(conn);
- break;
- }
- case PN_RAW_CONNECTION_NEED_WRITE_BUFFERS: {
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] PN_RAW_CONNECTION_NEED_WRITE_BUFFERS Need write buffers", conn->conn_id);
- break;
- }
- case PN_RAW_CONNECTION_NEED_READ_BUFFERS: {
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] PN_RAW_CONNECTION_NEED_READ_BUFFERS Need read buffers", conn->conn_id);
- grant_read_buffers(conn);
- break;
- }
- case PN_RAW_CONNECTION_WAKE: {
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] PN_RAW_CONNECTION_WAKE Wake-up", conn->conn_id);
- if (CLEAR_ATOMIC_FLAG(&conn->q2_restart)) {
- conn->q2_blocked = false;
- qd_log(http2_adaptor->protocol_log_source, QD_LOG_TRACE, "[C%"PRIu64"] q2 is unblocked on this connection", conn->conn_id);
- handle_incoming_http(conn);
- }
-
- while (qdr_connection_process(conn->qdr_conn)) {}
- break;
- }
- case PN_RAW_CONNECTION_READ: {
- // We don't want to read when we are q2 blocked.
- if (conn->q2_blocked) {
- return;
- }
-
- int read = handle_incoming_http(conn);
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] PN_RAW_CONNECTION_READ Read %i bytes", conn->conn_id, read);
- break;
- }
- case PN_RAW_CONNECTION_WRITTEN: {
- pn_raw_buffer_t buffs[WRITE_BUFFERS];
- size_t n;
- size_t written = 0;
-
- if (conn->pn_raw_conn == 0) {
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] PN_RAW_CONNECTION_WRITTEN, No pn_raw_conn", conn->conn_id);
- break;
- }
- while ( (n = pn_raw_connection_take_written_buffers(conn->pn_raw_conn, buffs, WRITE_BUFFERS)) ) {
- for (size_t i = 0; i < n; ++i) {
- written += buffs[i].size;
- qd_http2_buffer_t *qd_http2_buff = (qd_http2_buffer_t *) buffs[i].context;
- assert(qd_http2_buff);
- if (qd_http2_buff != NULL) {
- free_qd_http2_buffer_t(qd_http2_buff);
- }
- }
- }
-
- qd_log(log, QD_LOG_TRACE, "[C%"PRIu64"] PN_RAW_CONNECTION_WRITTEN Wrote %zu bytes, DEQ_SIZE(conn->buffs) = %zu", conn->conn_id, written, DEQ_SIZE(conn->buffs));
- restart_streams(conn);
- break;
- }
- default:
- break;
- }
-}
-
-
-static void handle_listener_event(pn_event_t *e, qd_server_t *qd_server, void *context) {
- qd_log_source_t *log = http2_adaptor->log_source;
-
- qd_http_listener_t *li = (qd_http_listener_t*) context;
- const char *host_port = li->config.host_port;
-
- switch (pn_event_type(e)) {
- case PN_LISTENER_OPEN: {
- qd_log(log, QD_LOG_NOTICE, "Listening on %s", host_port);
- }
- break;
-
- case PN_LISTENER_ACCEPT: {
- qd_log(log, QD_LOG_INFO, "Accepting HTTP connection on %s", host_port);
- qdr_http_connection_ingress(li);
- }
- break;
-
- case PN_LISTENER_CLOSE: {
- if (li->pn_listener) {
- pn_condition_t *cond = pn_listener_condition(li->pn_listener);
- if (pn_condition_is_set(cond)) {
- qd_log(log, QD_LOG_ERROR, "Listener error on %s: %s (%s)", host_port, pn_condition_get_description(cond), pn_condition_get_name(cond));
- }
- else {
- qd_log(log, QD_LOG_TRACE, "Listener closed on %s", host_port);
- }
- }
-
- sys_mutex_lock(http2_adaptor->lock);
- pn_listener_set_context(li->pn_listener, 0);
- li->pn_listener = 0;
- DEQ_REMOVE(http2_adaptor->listeners, li);
- sys_mutex_unlock(http2_adaptor->lock);
- qd_http_listener_decref(li);
- }
- break;
-
- default:
- break;
- }
-}
-
-/**
- * Delete connector via Management request
- */
-void qd_http2_delete_connector(qd_dispatch_t *qd, qd_http_connector_t *connector)
-{
- if (connector) {
- qd_log(http2_adaptor->log_source, QD_LOG_INFO, "Deleted HttpConnector for %s, %s:%s", connector->config.address, connector->config.host, connector->config.port);
-
- sys_mutex_lock(http2_adaptor->lock);
- DEQ_REMOVE(http2_adaptor->connectors, connector);
- sys_mutex_unlock(http2_adaptor->lock);
- //
- // Deleting a connector must delete the corresponding qdr_connection_t and qdr_http2_connection_t objects also.
- //
- if (connector->ctx)
- qdr_core_close_connection((qdr_connection_t *)connector->ctx);
- qd_http_connector_decref(connector);
- }
-}
-
-/**
- * Delete listener via Management request
- */
-void qd_http2_delete_listener(qd_dispatch_t *qd, qd_http_listener_t *li)
-{
- sys_mutex_lock(http2_adaptor->lock);
- if (li) {
- if (li->pn_listener) {
- pn_listener_close(li->pn_listener);
- }
- }
- sys_mutex_unlock(http2_adaptor->lock);
-}
-
-
-qd_http_listener_t *qd_http2_configure_listener(qd_dispatch_t *qd, const qd_http_bridge_config_t *config, qd_entity_t *entity)
-{
- qd_http_listener_t *li = qd_http_listener(qd->server, &handle_listener_event);
- if (!li) {
- qd_log(http2_adaptor->log_source, QD_LOG_ERROR, "Unable to create http listener: no memory");
- return 0;
- }
-
- li->config = *config;
- DEQ_INSERT_TAIL(http2_adaptor->listeners, li);
- qd_log(http2_adaptor->log_source, QD_LOG_INFO, "Configured http2_adaptor listener on %s", (&li->config)->host_port);
- pn_proactor_listen(qd_server_proactor(li->server), li->pn_listener, li->config.host_port, BACKLOG);
- return li;
-}
-
-
-qd_http_connector_t *qd_http2_configure_connector(qd_dispatch_t *qd, const qd_http_bridge_config_t *config, qd_entity_t *entity)
-{
- qd_http_connector_t *c = qd_http_connector(qd->server);
- if (!c) {
- qd_log(http2_adaptor->log_source, QD_LOG_ERROR, "Unable to create http connector: no memory");
- return 0;
- }
- c->config = *config;
- DEQ_ITEM_INIT(c);
- DEQ_INSERT_TAIL(http2_adaptor->connectors, c);
- qdr_http_connection_egress(c);
- return c;
-}
-
-/**
- * Called just before shutdown of the router. Frees listeners and connectors and any http2 buffers
- */
-static void qdr_http2_adaptor_final(void *adaptor_context)
-{
- qd_log(http2_adaptor->log_source, QD_LOG_TRACE, "Shutting down HTTP2 Protocol adaptor");
- qdr_http2_adaptor_t *adaptor = (qdr_http2_adaptor_t*) adaptor_context;
- qdr_protocol_adaptor_free(adaptor->core, adaptor->adaptor);
-
- // Free all remaining connections.
- qdr_http2_connection_t *http_conn = DEQ_HEAD(adaptor->connections);
- while (http_conn) {
- if (http_conn->stream_dispatcher_stream_data) {
- qd_log(http2_adaptor->log_source, QD_LOG_INFO, "[C%"PRIu64"] Freeing stream_data (stream_dispatcher, qdr_http2_adaptor_final) (%lx)", http_conn->conn_id, (long) http_conn->stream_dispatcher_stream_data);
- free_qdr_http2_stream_data_t(http_conn->stream_dispatcher_stream_data);
- http_conn->stream_dispatcher_stream_data = 0;
- }
- qd_log(http2_adaptor->log_source, QD_LOG_INFO, "[C%"PRIu64"] Freeing http2 connection (calling free_qdr_http2_connection)", http_conn->conn_id);
- clean_conn_buffs(http_conn);
- free_qdr_http2_connection(http_conn, true);
- http_conn = DEQ_HEAD(adaptor->connections);
- }
-
- // Free all http listeners
- qd_http_listener_t *li = DEQ_HEAD(adaptor->listeners);
- while (li) {
- DEQ_REMOVE_HEAD(adaptor->listeners);
- qd_http_listener_decref(li);
- li = DEQ_HEAD(adaptor->listeners);
- }
-
- // Free all http connectors
- qd_http_connector_t *ct = DEQ_HEAD(adaptor->connectors);
- while (ct) {
- DEQ_REMOVE_HEAD(adaptor->connectors);
- qd_http_connector_decref(ct);
- ct = DEQ_HEAD(adaptor->connectors);
- }
-
- sys_mutex_free(adaptor->lock);
- nghttp2_session_callbacks_del(adaptor->callbacks);
- http2_adaptor = NULL;
- free(adaptor);
-}
-
-/**
- * This initialization function will be invoked when the router core is ready for the protocol
- * adaptor to be created. This function:
- *
- * 1) Registers the protocol adaptor with the router-core.
- * 2) Prepares the protocol adaptor to be configured.
- * 3) Registers nghttp2 callbacks
- */
-static void qdr_http2_adaptor_init(qdr_core_t *core, void **adaptor_context)
-{
- qdr_http2_adaptor_t *adaptor = NEW(qdr_http2_adaptor_t);
- adaptor->core = core;
- adaptor->adaptor = qdr_protocol_adaptor(core,
- "http2", // name
- adaptor, // context
- qdr_http_activate,
- qdr_http_first_attach,
- qdr_http_second_attach,
- qdr_http_detach,
- qdr_http_flow,
- qdr_http_offer,
- qdr_http_drained,
- qdr_http_drain,
- qdr_http_push,
- qdr_http_deliver,
- qdr_http_get_credit,
- qdr_http_delivery_update,
- qdr_http_conn_close,
- qdr_http_conn_trace);
- adaptor->log_source = qd_log_source(QD_HTTP_LOG_SOURCE);
- adaptor->protocol_log_source = qd_log_source("PROTOCOL");
- adaptor->lock = sys_mutex();
- *adaptor_context = adaptor;
- DEQ_INIT(adaptor->listeners);
- DEQ_INIT(adaptor->connectors);
- DEQ_INIT(adaptor->connections);
-
- //
- // Register all nghttp2 callbacks.
- //
- nghttp2_session_callbacks *callbacks;
- nghttp2_session_callbacks_new(&callbacks);
-
- //
- // These callbacks are called when we feed the incoming binary http2 data from the client or the server to nghttp2_session_mem_recv()
- // in handle_incoming_http
- //
- nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback);
- nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks, on_begin_headers_callback);
- nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback);
- nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, on_data_chunk_recv_callback);
- nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(callbacks, on_invalid_frame_recv_callback);
-
-
- // These callbacks are called when you try to push out amqp data to http2
- // More specifically, they are called from handle_outgoing_http()
- nghttp2_session_callbacks_set_send_data_callback(callbacks, send_data_callback);
- nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
-
- // This is a general callback
- nghttp2_session_callbacks_set_error_callback2(callbacks, on_error_callback);
-
- adaptor->callbacks = callbacks;
- http2_adaptor = adaptor;
-}
-
-/**
- * Declare the adaptor so that it will self-register on process startup.
- */
-QDR_CORE_ADAPTOR_DECLARE("http2-adaptor", qdr_http2_adaptor_init, qdr_http2_adaptor_final)
diff --git a/src/adaptors/http2/http2_adaptor.h b/src/adaptors/http2/http2_adaptor.h
deleted file mode 100644
index 819c02d..0000000
--- a/src/adaptors/http2/http2_adaptor.h
... 10650 lines suppressed ...
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org