Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "native_test_server.h" | 5 #include "native_test_server.h" |
| 6 | 6 |
| 7 #include <string> | |
| 8 | |
| 7 #include "base/android/jni_android.h" | 9 #include "base/android/jni_android.h" |
| 8 #include "base/android/jni_string.h" | 10 #include "base/android/jni_string.h" |
| 11 #include "base/android/path_utils.h" | |
| 9 #include "base/bind.h" | 12 #include "base/bind.h" |
| 10 #include "base/files/file_path.h" | 13 #include "base/files/file_path.h" |
| 14 #include "base/files/file_util.h" | |
| 15 #include "base/macros.h" | |
| 11 #include "base/memory/scoped_ptr.h" | 16 #include "base/memory/scoped_ptr.h" |
| 12 #include "base/path_service.h" | 17 #include "base/path_service.h" |
| 18 #include "base/strings/string_split.h" | |
| 13 #include "base/strings/string_util.h" | 19 #include "base/strings/string_util.h" |
| 20 #include "base/strings/stringprintf.h" | |
| 21 #include "components/cronet/android/cronet_url_request_context_adapter.h" | |
| 22 #include "components/cronet/android/url_request_context_adapter.h" | |
| 14 #include "jni/NativeTestServer_jni.h" | 23 #include "jni/NativeTestServer_jni.h" |
| 24 #include "net/dns/host_resolver_impl.h" | |
| 25 #include "net/dns/mock_host_resolver.h" | |
| 15 #include "net/http/http_status_code.h" | 26 #include "net/http/http_status_code.h" |
| 16 #include "net/test/embedded_test_server/embedded_test_server.h" | 27 #include "net/test/embedded_test_server/embedded_test_server.h" |
| 17 #include "net/test/embedded_test_server/http_request.h" | 28 #include "net/test/embedded_test_server/http_request.h" |
| 18 #include "net/test/embedded_test_server/http_response.h" | 29 #include "net/test/embedded_test_server/http_response.h" |
| 19 #include "url/gurl.h" | 30 #include "url/gurl.h" |
| 20 | 31 |
| 21 namespace cronet { | 32 namespace cronet { |
| 22 | 33 |
| 23 namespace { | 34 namespace { |
| 24 | 35 |
| 25 const char echo_body_path[] = "/echo_body"; | 36 const char echo_body_path[] = "/echo_body"; |
| 26 const char echo_header_path[] = "/echo_header"; | 37 const char echo_header_path[] = "/echo_header"; |
| 27 const char echo_all_headers_path[] = "/echo_all_headers"; | 38 const char echo_all_headers_path[] = "/echo_all_headers"; |
| 28 const char echo_method_path[] = "/echo_method"; | 39 const char echo_method_path[] = "/echo_method"; |
| 29 const char redirect_to_echo_body_path[] = "/redirect_to_echo_body"; | 40 const char redirect_to_echo_body_path[] = "/redirect_to_echo_body"; |
| 41 const char fake_sdch_domain[] = "fake.sdch.domain"; | |
| 42 // Path that advertises the dictionaries passed in query params if client | |
| 43 // supports Sdch encoding. E.g. /sdch/index?q=LeQxM80O will make the server | |
| 44 // responds with "Get-Dictionary: /sdch/dict/LeQxM80O". | |
| 45 const char sdch_path[] = "/sdch/index"; | |
| 46 // Path that returns encoded response if client has the right dictionary. | |
| 47 const char sdch_test_path[] = "/sdch/test"; | |
| 48 // Path where dictionaries are stored. | |
| 49 const char sdch_dict_path[] = "/sdch/dict/"; | |
| 30 | 50 |
| 31 net::test_server::EmbeddedTestServer* g_test_server = nullptr; | 51 net::test_server::EmbeddedTestServer* g_test_server = nullptr; |
| 32 | 52 |
| 33 scoped_ptr<net::test_server::HttpResponse> UploadServerRequestHandler( | 53 class CustomHttpResponse : public net::test_server::HttpResponse { |
| 54 public: | |
| 55 CustomHttpResponse(const std::string& headers, const std::string& contents) | |
| 56 : headers_(headers), contents_(contents) {} | |
| 57 | |
| 58 std::string ToResponseString() const override { | |
| 59 return headers_ + "\r\n" + contents_; | |
| 60 } | |
| 61 | |
| 62 void AddHeader(const std::string& key_value_pair) { | |
| 63 headers_.append(base::StringPrintf("%s\r\n", key_value_pair.c_str())); | |
| 64 } | |
| 65 | |
| 66 private: | |
| 67 std::string headers_; | |
| 68 std::string contents_; | |
| 69 | |
| 70 DISALLOW_COPY_AND_ASSIGN(CustomHttpResponse); | |
| 71 }; | |
| 72 | |
| 73 scoped_ptr<CustomHttpResponse> ConstructResponseBasedOnFile( | |
| 74 const base::FilePath& file_path) { | |
| 75 std::string file_contents; | |
| 76 bool read_file = base::ReadFileToString(file_path, &file_contents); | |
| 77 DCHECK(read_file); | |
| 78 base::FilePath headers_path( | |
| 79 file_path.AddExtension(FILE_PATH_LITERAL("mock-http-headers"))); | |
| 80 std::string headers_contents; | |
| 81 bool read_headers = base::ReadFileToString(headers_path, &headers_contents); | |
| 82 DCHECK(read_headers); | |
| 83 scoped_ptr<CustomHttpResponse> http_response( | |
| 84 new CustomHttpResponse(headers_contents, file_contents)); | |
| 85 return http_response.Pass(); | |
| 86 } | |
| 87 | |
| 88 scoped_ptr<net::test_server::HttpResponse> NativeTestServerRequestHandler( | |
| 34 const net::test_server::HttpRequest& request) { | 89 const net::test_server::HttpRequest& request) { |
| 35 DCHECK(g_test_server); | 90 DCHECK(g_test_server); |
| 36 scoped_ptr<net::test_server::BasicHttpResponse> response( | 91 scoped_ptr<net::test_server::BasicHttpResponse> response( |
| 37 new net::test_server::BasicHttpResponse()); | 92 new net::test_server::BasicHttpResponse()); |
| 38 response->set_content_type("text/plain"); | 93 response->set_content_type("text/plain"); |
| 39 | 94 |
| 40 if (request.relative_url == echo_body_path) { | 95 if (request.relative_url == echo_body_path) { |
| 41 if (request.has_content) { | 96 if (request.has_content) { |
| 42 response->set_content(request.content); | 97 response->set_content(request.content); |
| 43 } else { | 98 } else { |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 70 if (request.relative_url == redirect_to_echo_body_path) { | 125 if (request.relative_url == redirect_to_echo_body_path) { |
| 71 response->set_code(net::HTTP_TEMPORARY_REDIRECT); | 126 response->set_code(net::HTTP_TEMPORARY_REDIRECT); |
| 72 response->AddCustomHeader("Location", echo_body_path); | 127 response->AddCustomHeader("Location", echo_body_path); |
| 73 return response.Pass(); | 128 return response.Pass(); |
| 74 } | 129 } |
| 75 | 130 |
| 76 // Unhandled requests result in the Embedded test server sending a 404. | 131 // Unhandled requests result in the Embedded test server sending a 404. |
| 77 return scoped_ptr<net::test_server::BasicHttpResponse>(); | 132 return scoped_ptr<net::test_server::BasicHttpResponse>(); |
| 78 } | 133 } |
| 79 | 134 |
| 135 scoped_ptr<net::test_server::HttpResponse> SdchRequestHandler( | |
| 136 const net::test_server::HttpRequest& request) { | |
| 137 DCHECK(g_test_server); | |
| 138 base::FilePath dir_path; | |
| 139 bool get_data_dir = base::android::GetDataDirectory(&dir_path); | |
| 140 DCHECK(get_data_dir); | |
| 141 dir_path = dir_path.Append(FILE_PATH_LITERAL("test")); | |
| 142 | |
| 143 if (StartsWithASCII(request.relative_url, sdch_path, true)) { | |
| 144 base::FilePath file_path = dir_path.Append("sdch/index"); | |
| 145 scoped_ptr<CustomHttpResponse> response = | |
| 146 ConstructResponseBasedOnFile(file_path).Pass(); | |
| 147 // Check for query params to see which dictionaries to advertise. | |
|
mef
2015/04/28 20:37:11
Need comment on what query params are used to defi
xunjieli
2015/04/29 02:05:09
Done.
| |
| 148 GURL url = g_test_server->GetURL(request.relative_url); | |
| 149 base::StringPairs pairs; | |
| 150 if (!base::SplitStringIntoKeyValuePairs(url.query(), '=', '&', &pairs)) { | |
|
mef
2015/04/28 20:37:11
Suggest: using GetValueForKeyInQuery()
xunjieli
2015/04/29 02:05:09
Done. Was thinking we should support multiple dict
| |
| 151 DCHECK(false) << "parsing query params failed"; | |
| 152 } | |
| 153 auto it = request.headers.find("Accept-Encoding"); | |
|
mef
2015/04/28 20:37:10
'it' is a bit generic, suggest calling it 'accept_
xunjieli
2015/04/29 02:05:09
Done.
| |
| 154 if (it != request.headers.end()) { | |
| 155 if (it->second.find("sdch") != std::string::npos) { | |
| 156 std::string get_dictionary_header = ""; | |
| 157 for (base::StringPairs::const_iterator it = pairs.begin(); | |
|
mef
2015/04/28 20:37:11
This 'it' masks upper level 'it', which makes it c
xunjieli
2015/04/29 02:05:09
Done.
| |
| 158 it != pairs.end(); ++it) { | |
|
mef
2015/04/28 20:37:10
Could we use range-based for loop here?
Why do we
xunjieli
2015/04/29 02:05:09
Done. Was thinking of supporting multiple dictiona
| |
| 159 get_dictionary_header += sdch_dict_path + it->second + ", "; | |
| 160 } | |
| 161 if (!get_dictionary_header.empty()) { | |
| 162 // Remove trailing comma and space. | |
| 163 get_dictionary_header = | |
| 164 get_dictionary_header.substr(0, get_dictionary_header.size() - 2); | |
| 165 response->AddHeader("Get-Dictionary: " + get_dictionary_header); | |
| 166 } | |
| 167 } | |
| 168 } | |
| 169 return response.Pass(); | |
| 170 } | |
| 171 | |
| 172 if (StartsWithASCII(request.relative_url, sdch_test_path, true)) { | |
| 173 auto it = request.headers.find("Avail-Dictionary"); | |
|
mef
2015/04/28 20:37:10
more descriptive name than 'it'?
xunjieli
2015/04/29 02:05:09
Done.
| |
| 174 if (it != request.headers.end()) { | |
| 175 base::FilePath file_path = | |
| 176 dir_path.Append("sdch/" + it->second + "_encoded"); | |
| 177 return ConstructResponseBasedOnFile(file_path).Pass(); | |
| 178 } | |
| 179 scoped_ptr<net::test_server::BasicHttpResponse> response( | |
| 180 new net::test_server::BasicHttpResponse()); | |
| 181 response->set_content_type("text/plain"); | |
| 182 response->set_content("Sdch is not used.\n"); | |
|
mef
2015/04/28 20:37:10
maybe construct response based on another file wit
xunjieli
2015/04/29 02:05:09
Hmm.. but we just want to make sure here that sdch
mef
2015/04/29 15:37:29
sg, thanks!
| |
| 183 return response.Pass(); | |
| 184 } | |
| 185 | |
| 186 // Unhandled requests result in the Embedded test server sending a 404. | |
| 187 return scoped_ptr<net::test_server::BasicHttpResponse>(); | |
| 188 } | |
| 189 | |
| 190 void RegisterHostResolverProcHelper( | |
| 191 net::URLRequestContext* url_request_context) { | |
| 192 net::HostResolverImpl* resolver = | |
| 193 static_cast<net::HostResolverImpl*>(url_request_context->host_resolver()); | |
| 194 scoped_refptr<net::RuleBasedHostResolverProc> proc = | |
| 195 new net::RuleBasedHostResolverProc(NULL); | |
| 196 proc->AddRule(fake_sdch_domain, "127.0.0.1"); | |
| 197 resolver->set_proc_params_for_test( | |
| 198 net::HostResolverImpl::ProcTaskParams(proc.get(), 1u)); | |
| 199 JNIEnv* env = base::android::AttachCurrentThread(); | |
| 200 Java_NativeTestServer_onHostResolverProcRegistered(env); | |
| 201 } | |
| 202 | |
| 203 void RegisterHostResolverProcOnNetworkThread( | |
| 204 CronetURLRequestContextAdapter* context_adapter) { | |
| 205 RegisterHostResolverProcHelper(context_adapter->GetURLRequestContext()); | |
| 206 } | |
| 207 | |
| 208 // TODO(xunjieli): Delete this once legacy API is removed. | |
| 209 void RegisterHostResolverProcOnNetworkThreadLegacyAPI( | |
| 210 URLRequestContextAdapter* context_adapter) { | |
| 211 RegisterHostResolverProcHelper(context_adapter->GetURLRequestContext()); | |
| 212 } | |
| 213 | |
| 80 } // namespace | 214 } // namespace |
| 81 | 215 |
| 82 jboolean StartNativeTestServer(JNIEnv* env, | 216 jboolean StartNativeTestServer(JNIEnv* env, |
| 83 jclass jcaller, | 217 jclass jcaller, |
| 84 jstring jtest_files_root) { | 218 jstring jtest_files_root) { |
| 85 // Shouldn't happen. | 219 // Shouldn't happen. |
| 86 if (g_test_server) | 220 if (g_test_server) |
| 87 return false; | 221 return false; |
| 88 g_test_server = new net::test_server::EmbeddedTestServer(); | 222 g_test_server = new net::test_server::EmbeddedTestServer(); |
| 89 g_test_server->RegisterRequestHandler( | 223 g_test_server->RegisterRequestHandler( |
| 90 base::Bind(&UploadServerRequestHandler)); | 224 base::Bind(&NativeTestServerRequestHandler)); |
| 91 // Add a second handler for paths that UploadServerRequestHandler does not | 225 g_test_server->RegisterRequestHandler(base::Bind(&SdchRequestHandler)); |
| 92 // handle. | |
| 93 base::FilePath test_files_root( | 226 base::FilePath test_files_root( |
| 94 base::android::ConvertJavaStringToUTF8(env, jtest_files_root)); | 227 base::android::ConvertJavaStringToUTF8(env, jtest_files_root)); |
| 228 | |
| 229 // Add a third handler for paths that NativeTestServerRequestHandler does not | |
| 230 // handle. | |
| 95 g_test_server->ServeFilesFromDirectory(test_files_root); | 231 g_test_server->ServeFilesFromDirectory(test_files_root); |
| 96 return g_test_server->InitializeAndWaitUntilReady(); | 232 return g_test_server->InitializeAndWaitUntilReady(); |
| 97 } | 233 } |
| 98 | 234 |
| 235 void RegisterHostResolverProc(JNIEnv* env, | |
| 236 jclass jcaller, | |
| 237 jlong jadapter, | |
| 238 jboolean jlegacy_api) { | |
| 239 if (jlegacy_api == JNI_TRUE) { | |
| 240 URLRequestContextAdapter* context_adapter = | |
| 241 reinterpret_cast<URLRequestContextAdapter*>(jadapter); | |
| 242 context_adapter->PostTaskToNetworkThread( | |
| 243 FROM_HERE, base::Bind(&RegisterHostResolverProcOnNetworkThreadLegacyAPI, | |
| 244 base::Unretained(context_adapter))); | |
| 245 } else { | |
| 246 CronetURLRequestContextAdapter* context_adapter = | |
| 247 reinterpret_cast<CronetURLRequestContextAdapter*>(jadapter); | |
| 248 context_adapter->PostTaskToNetworkThread( | |
| 249 FROM_HERE, base::Bind(&RegisterHostResolverProcOnNetworkThread, | |
| 250 base::Unretained(context_adapter))); | |
| 251 } | |
| 252 } | |
| 253 | |
| 99 void ShutdownNativeTestServer(JNIEnv* env, jclass jcaller) { | 254 void ShutdownNativeTestServer(JNIEnv* env, jclass jcaller) { |
| 100 if (!g_test_server) | 255 if (!g_test_server) |
| 101 return; | 256 return; |
| 102 delete g_test_server; | 257 delete g_test_server; |
| 103 g_test_server = NULL; | 258 g_test_server = NULL; |
| 104 } | 259 } |
| 105 | 260 |
| 106 jstring GetEchoBodyURL(JNIEnv* env, jclass jcaller) { | 261 jstring GetEchoBodyURL(JNIEnv* env, jclass jcaller) { |
| 107 DCHECK(g_test_server); | 262 DCHECK(g_test_server); |
| 108 GURL url = g_test_server->GetURL(echo_body_path); | 263 GURL url = g_test_server->GetURL(echo_body_path); |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 137 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); | 292 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); |
| 138 } | 293 } |
| 139 | 294 |
| 140 jstring GetFileURL(JNIEnv* env, jclass jcaller, jstring jfile_path) { | 295 jstring GetFileURL(JNIEnv* env, jclass jcaller, jstring jfile_path) { |
| 141 DCHECK(g_test_server); | 296 DCHECK(g_test_server); |
| 142 std::string file = base::android::ConvertJavaStringToUTF8(env, jfile_path); | 297 std::string file = base::android::ConvertJavaStringToUTF8(env, jfile_path); |
| 143 GURL url = g_test_server->GetURL(file); | 298 GURL url = g_test_server->GetURL(file); |
| 144 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); | 299 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); |
| 145 } | 300 } |
| 146 | 301 |
| 302 jstring GetSdchURL(JNIEnv* env, jclass jcaller) { | |
| 303 DCHECK(g_test_server); | |
| 304 std::string url(base::StringPrintf("http://%s:%d", fake_sdch_domain, | |
| 305 g_test_server->port())); | |
| 306 return base::android::ConvertUTF8ToJavaString(env, url).Release(); | |
| 307 } | |
| 308 | |
| 147 bool RegisterNativeTestServer(JNIEnv* env) { | 309 bool RegisterNativeTestServer(JNIEnv* env) { |
| 148 return RegisterNativesImpl(env); | 310 return RegisterNativesImpl(env); |
| 149 } | 311 } |
| 150 | 312 |
| 151 } // namespace cronet | 313 } // namespace cronet |
| OLD | NEW |