1 /*
2 * This File is part of Davix, The IO library for HTTP based protocols
3 * Copyright (C) CERN 2013
4 * Author: Adrien Devresse <adrien.devresse@cern.ch>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22 #include <string>
23 #include <algorithm>
24 #include <mutex>
25 #include <davix_internal.hpp>
26 #include "neonsessionfactory.hpp"
27
28 #include <utils/davix_logger_internal.hpp>
29
30 namespace Davix {
31
32 static std::once_flag neon_once;
33
init_neon()34 static void init_neon(){
35 ne_sock_init();
36 }
37
sessionCachingDisabled()38 static bool sessionCachingDisabled(){
39 return ( getenv("DAVIX_DISABLE_SESSION_CACHING") != NULL);
40 }
41
NEONSessionFactory()42 NEONSessionFactory::NEONSessionFactory() :
43 _sess_map(),
44 _sess_mut(),
45 _session_caching(!sessionCachingDisabled())
46 {
47 std::call_once(neon_once, &init_neon);
48 DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_CORE, "HTTP/SSL Session caching {}", (_session_caching?"ENABLED":"DISABLED"));
49 }
50
~NEONSessionFactory()51 NEONSessionFactory::~NEONSessionFactory(){
52 std::lock_guard<std::mutex> lock(_sess_mut);
53 for(std::multimap<std::string, ne_session*>::iterator it = _sess_map.begin(); it != _sess_map.end(); ++it){
54 ne_session_destroy(it->second);
55 }
56 }
57
davix_session_uri_rewrite(const Uri & u)58 inline std::string davix_session_uri_rewrite(const Uri & u){
59 std::string proto = u.getProtocol();
60 if(proto.compare(0,4, "http") ==0
61 || proto.compare(0,2, "s3") == 0
62 || proto.compare(0,3, "dav") == 0
63 || proto.compare(0, 6, "gcloud") == 0){
64 proto.assign("http");
65 if(*(u.getProtocol().rbegin()) == 's')
66 proto.append("s");
67 return proto;
68 }
69 return std::string();
70 }
71
72 //------------------------------------------------------------------------------
73 // Create a NEONSession.
74 //------------------------------------------------------------------------------
provideNEONSession(const Uri & uri,const RequestParams & params,DavixError ** err)75 std::unique_ptr<NEONSession> NEONSessionFactory::provideNEONSession(const Uri &uri, const RequestParams ¶ms, DavixError **err) {
76 return std::unique_ptr<NEONSession>(new NEONSession(*this, uri, params, err));
77 }
78
createNeonSession(const RequestParams & params,const Uri & uri,DavixError ** err)79 ne_session* NEONSessionFactory::createNeonSession(const RequestParams & params, const Uri & uri, DavixError **err){
80 if(uri.getStatus() == StatusCode::OK){
81 std::string scheme = davix_session_uri_rewrite(uri);
82 if(scheme.size() > 0){
83 return create_recycled_session(params, scheme, uri.getHost(), httpUriGetPort(uri));
84 }
85 }
86
87 DavixError::setupError(err, davix_scope_http_request(), StatusCode::UriParsingError, fmt::format("impossible to parse {}, not a valid HTTP, S3 or Webdav URL", uri.getString()));
88 return NULL;
89 }
90
storeNeonSession(ne_session * sess)91 void NEONSessionFactory::storeNeonSession(ne_session* sess){
92 internal_release_session_handle(sess);
93 }
94
create_session(const RequestParams & params,const std::string & protocol,const std::string & host,unsigned int port)95 ne_session* NEONSessionFactory::create_session(const RequestParams & params, const std::string & protocol, const std::string &host, unsigned int port){
96 ne_session *se;
97 se = ne_session_create(protocol.c_str(), host.c_str(), (int) port);
98
99 const Uri* proxy = params.getProxyServer();
100 if(se != NULL && proxy != NULL){
101 DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_HTTP, " configure mandatory proxy to {}", proxy->getString().c_str());
102 const enum ne_sock_sversion version = ((proxy->getProtocol().compare("socks5") ==0)?NE_SOCK_SOCKSV5:NE_SOCK_SOCKSV4);
103 const int port_proxy = ((proxy->getPort() ==0)?1080:(proxy->getPort()));
104 const std::string & userinfo = proxy->getUserInfo();
105 std::string user, password;
106 std::string::const_iterator delimiter = std::find(userinfo.begin(), userinfo.end(), ':');
107
108 if(delimiter != userinfo.end()){
109 user.assign(std::string(userinfo.begin(), delimiter));
110 password.assign((delimiter+1), userinfo.end());
111 ne_session_socks_proxy(se, version, proxy->getHost().c_str(), port_proxy, user.c_str(), password.c_str());
112 }else{
113 ne_session_socks_proxy(se, version, proxy->getHost().c_str(), port_proxy, NULL, NULL);
114 }
115
116 }
117 //ne_ssl_trust_default_ca(se); not stable in neon on epel 5
118 return se;
119 }
120
create_recycled_session(const RequestParams & params,const std::string & protocol,const std::string & host,unsigned int port)121 ne_session* NEONSessionFactory::create_recycled_session(const RequestParams & params, const std::string &protocol, const std::string &host, unsigned int port){
122
123 if(params.getKeepAlive()){
124 ne_session* se= NULL;
125 std::lock_guard<std::mutex> lock(_sess_mut);
126 std::multimap<std::string, ne_session*>::iterator it;
127 if( (it = _sess_map.find(create_map_keys_from_URL(protocol, host, port))) != _sess_map.end()){
128 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_HTTP, "cached ne_session found ! taken from cache ");
129 se = it->second;
130 _sess_map.erase(it);
131 return se;
132 }
133
134 }
135 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_HTTP, "no cached ne_session, create a new one ");
136 return create_session(params, protocol, host, port);
137 }
138
setSessionCaching(bool caching)139 void NEONSessionFactory::setSessionCaching(bool caching){
140 _session_caching = caching && !sessionCachingDisabled();
141 }
142
internal_release_session_handle(ne_session * sess)143 void NEONSessionFactory::internal_release_session_handle(ne_session* sess){
144 // clear sensitive data
145 // none
146 //
147 std::lock_guard<std::mutex> lock(_sess_mut);
148 std::multimap<std::string, ne_session*>::iterator it;
149 std::string sess_key;
150 sess_key.append(ne_get_scheme(sess)).append(ne_get_server_hostport(sess));
151
152 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_HTTP, "add old session to cache {}", sess_key.c_str());
153
154 _sess_map.insert(std::pair<std::string, ne_session*>(sess_key, sess));
155 }
156
create_map_keys_from_URL(const std::string & protocol,const std::string & host,unsigned int port)157 std::string create_map_keys_from_URL(const std::string & protocol, const std::string &host, unsigned int port){
158 std::string host_port;
159 if( (strcmp(protocol.c_str(), "http") ==0 && port == 80)
160 || ( strcmp(protocol.c_str(), "https") ==0 && port == 443)){
161 host_port = fmt::format("{}{}", protocol, host);
162 }else
163 host_port = fmt::format("{}{}:{}", protocol, host, port);
164 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_HTTP, " creating session keys... {}", host_port);
165 return host_port;
166 }
167
168 } // namespace Davix
169