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 &params, 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