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" |
13 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
| 19 #include "base/strings/stringprintf.h" |
| 20 #include "components/cronet/android/cronet_url_request_context_adapter.h" |
| 21 #include "components/cronet/android/url_request_context_adapter.h" |
14 #include "jni/NativeTestServer_jni.h" | 22 #include "jni/NativeTestServer_jni.h" |
| 23 #include "net/base/url_util.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 kEchoBodyPath[] = "/echo_body"; |
26 const char echo_header_path[] = "/echo_header"; | 37 const char kEchoHeaderPath[] = "/echo_header"; |
27 const char echo_all_headers_path[] = "/echo_all_headers"; | 38 const char kEchoAllHeadersPath[] = "/echo_all_headers"; |
28 const char echo_method_path[] = "/echo_method"; | 39 const char kEchoMethodPath[] = "/echo_method"; |
29 const char redirect_to_echo_body_path[] = "/redirect_to_echo_body"; | 40 const char kRedirectToEchoBodyPath[] = "/redirect_to_echo_body"; |
| 41 const char kFakeSdchDomain[] = "fake.sdch.domain"; |
| 42 // Path that advertises the dictionary 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 kSdchPath[] = "/sdch/index"; |
| 46 // Path that returns encoded response if client has the right dictionary. |
| 47 const char kSdchTestPath[] = "/sdch/test"; |
| 48 // Path where dictionaries are stored. |
| 49 const char kSdchDictPath[] = "/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 == kEchoBodyPath) { |
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 { |
44 response->set_content("Request has no body. :("); | 99 response->set_content("Request has no body. :("); |
45 } | 100 } |
46 return response.Pass(); | 101 return response.Pass(); |
47 } | 102 } |
48 | 103 |
49 if (StartsWithASCII(request.relative_url, echo_header_path, true)) { | 104 if (StartsWithASCII(request.relative_url, kEchoHeaderPath, true)) { |
50 GURL url = g_test_server->GetURL(request.relative_url); | 105 GURL url = g_test_server->GetURL(request.relative_url); |
51 auto it = request.headers.find(url.query()); | 106 auto it = request.headers.find(url.query()); |
52 if (it != request.headers.end()) { | 107 if (it != request.headers.end()) { |
53 response->set_content(it->second); | 108 response->set_content(it->second); |
54 } else { | 109 } else { |
55 response->set_content("Header not found. :("); | 110 response->set_content("Header not found. :("); |
56 } | 111 } |
57 return response.Pass(); | 112 return response.Pass(); |
58 } | 113 } |
59 | 114 |
60 if (request.relative_url == echo_all_headers_path) { | 115 if (request.relative_url == kEchoAllHeadersPath) { |
61 response->set_content(request.all_headers); | 116 response->set_content(request.all_headers); |
62 return response.Pass(); | 117 return response.Pass(); |
63 } | 118 } |
64 | 119 |
65 if (request.relative_url == echo_method_path) { | 120 if (request.relative_url == kEchoMethodPath) { |
66 response->set_content(request.method_string); | 121 response->set_content(request.method_string); |
67 return response.Pass(); | 122 return response.Pass(); |
68 } | 123 } |
69 | 124 |
70 if (request.relative_url == redirect_to_echo_body_path) { | 125 if (request.relative_url == kRedirectToEchoBodyPath) { |
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", kEchoBodyPath); |
73 return response.Pass(); | 128 return response.Pass(); |
74 } | 129 } |
75 | 130 |
| 131 // Unhandled requests result in the Embedded test server sending a 404. |
| 132 return scoped_ptr<net::test_server::BasicHttpResponse>(); |
| 133 } |
| 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, kSdchPath, 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 dictionary to advertise. |
| 148 // For instance, ?q=dictionaryA will make the server advertise dictionaryA. |
| 149 GURL url = g_test_server->GetURL(request.relative_url); |
| 150 std::string dictionary; |
| 151 if (!net::GetValueForKeyInQuery(url, "q", &dictionary)) { |
| 152 CHECK(false) << "dictionary is not found in query params of " |
| 153 << request.relative_url; |
| 154 } |
| 155 auto accept_encoding_header = request.headers.find("Accept-Encoding"); |
| 156 if (accept_encoding_header != request.headers.end()) { |
| 157 if (accept_encoding_header->second.find("sdch") != std::string::npos) |
| 158 response->AddHeader(base::StringPrintf( |
| 159 "Get-Dictionary: %s%s", kSdchDictPath, dictionary.c_str())); |
| 160 } |
| 161 return response.Pass(); |
| 162 } |
| 163 |
| 164 if (StartsWithASCII(request.relative_url, kSdchTestPath, true)) { |
| 165 auto avail_dictionary_header = request.headers.find("Avail-Dictionary"); |
| 166 if (avail_dictionary_header != request.headers.end()) { |
| 167 base::FilePath file_path = dir_path.Append( |
| 168 "sdch/" + avail_dictionary_header->second + "_encoded"); |
| 169 return ConstructResponseBasedOnFile(file_path).Pass(); |
| 170 } |
| 171 scoped_ptr<net::test_server::BasicHttpResponse> response( |
| 172 new net::test_server::BasicHttpResponse()); |
| 173 response->set_content_type("text/plain"); |
| 174 response->set_content("Sdch is not used.\n"); |
| 175 return response.Pass(); |
| 176 } |
| 177 |
76 // Unhandled requests result in the Embedded test server sending a 404. | 178 // Unhandled requests result in the Embedded test server sending a 404. |
77 return scoped_ptr<net::test_server::BasicHttpResponse>(); | 179 return scoped_ptr<net::test_server::BasicHttpResponse>(); |
78 } | 180 } |
79 | 181 |
| 182 void RegisterHostResolverProcHelper( |
| 183 net::URLRequestContext* url_request_context) { |
| 184 net::HostResolverImpl* resolver = |
| 185 static_cast<net::HostResolverImpl*>(url_request_context->host_resolver()); |
| 186 scoped_refptr<net::RuleBasedHostResolverProc> proc = |
| 187 new net::RuleBasedHostResolverProc(NULL); |
| 188 proc->AddRule(kFakeSdchDomain, "127.0.0.1"); |
| 189 resolver->set_proc_params_for_test( |
| 190 net::HostResolverImpl::ProcTaskParams(proc.get(), 1u)); |
| 191 JNIEnv* env = base::android::AttachCurrentThread(); |
| 192 Java_NativeTestServer_onHostResolverProcRegistered(env); |
| 193 } |
| 194 |
| 195 void RegisterHostResolverProcOnNetworkThread( |
| 196 CronetURLRequestContextAdapter* context_adapter) { |
| 197 RegisterHostResolverProcHelper(context_adapter->GetURLRequestContext()); |
| 198 } |
| 199 |
| 200 // TODO(xunjieli): Delete this once legacy API is removed. |
| 201 void RegisterHostResolverProcOnNetworkThreadLegacyAPI( |
| 202 URLRequestContextAdapter* context_adapter) { |
| 203 RegisterHostResolverProcHelper(context_adapter->GetURLRequestContext()); |
| 204 } |
| 205 |
80 } // namespace | 206 } // namespace |
81 | 207 |
82 jboolean StartNativeTestServer(JNIEnv* env, | 208 jboolean StartNativeTestServer(JNIEnv* env, |
83 jclass jcaller, | 209 jclass jcaller, |
84 jstring jtest_files_root) { | 210 jstring jtest_files_root) { |
85 // Shouldn't happen. | 211 // Shouldn't happen. |
86 if (g_test_server) | 212 if (g_test_server) |
87 return false; | 213 return false; |
88 g_test_server = new net::test_server::EmbeddedTestServer(); | 214 g_test_server = new net::test_server::EmbeddedTestServer(); |
89 g_test_server->RegisterRequestHandler( | 215 g_test_server->RegisterRequestHandler( |
90 base::Bind(&UploadServerRequestHandler)); | 216 base::Bind(&NativeTestServerRequestHandler)); |
91 // Add a second handler for paths that UploadServerRequestHandler does not | 217 g_test_server->RegisterRequestHandler(base::Bind(&SdchRequestHandler)); |
92 // handle. | |
93 base::FilePath test_files_root( | 218 base::FilePath test_files_root( |
94 base::android::ConvertJavaStringToUTF8(env, jtest_files_root)); | 219 base::android::ConvertJavaStringToUTF8(env, jtest_files_root)); |
| 220 |
| 221 // Add a third handler for paths that NativeTestServerRequestHandler does not |
| 222 // handle. |
95 g_test_server->ServeFilesFromDirectory(test_files_root); | 223 g_test_server->ServeFilesFromDirectory(test_files_root); |
96 return g_test_server->InitializeAndWaitUntilReady(); | 224 return g_test_server->InitializeAndWaitUntilReady(); |
97 } | 225 } |
98 | 226 |
| 227 void RegisterHostResolverProc(JNIEnv* env, |
| 228 jclass jcaller, |
| 229 jlong jadapter, |
| 230 jboolean jlegacy_api) { |
| 231 if (jlegacy_api == JNI_TRUE) { |
| 232 URLRequestContextAdapter* context_adapter = |
| 233 reinterpret_cast<URLRequestContextAdapter*>(jadapter); |
| 234 context_adapter->PostTaskToNetworkThread( |
| 235 FROM_HERE, base::Bind(&RegisterHostResolverProcOnNetworkThreadLegacyAPI, |
| 236 base::Unretained(context_adapter))); |
| 237 } else { |
| 238 CronetURLRequestContextAdapter* context_adapter = |
| 239 reinterpret_cast<CronetURLRequestContextAdapter*>(jadapter); |
| 240 context_adapter->PostTaskToNetworkThread( |
| 241 FROM_HERE, base::Bind(&RegisterHostResolverProcOnNetworkThread, |
| 242 base::Unretained(context_adapter))); |
| 243 } |
| 244 } |
| 245 |
99 void ShutdownNativeTestServer(JNIEnv* env, jclass jcaller) { | 246 void ShutdownNativeTestServer(JNIEnv* env, jclass jcaller) { |
100 if (!g_test_server) | 247 if (!g_test_server) |
101 return; | 248 return; |
102 delete g_test_server; | 249 delete g_test_server; |
103 g_test_server = NULL; | 250 g_test_server = NULL; |
104 } | 251 } |
105 | 252 |
106 jstring GetEchoBodyURL(JNIEnv* env, jclass jcaller) { | 253 jstring GetEchoBodyURL(JNIEnv* env, jclass jcaller) { |
107 DCHECK(g_test_server); | 254 DCHECK(g_test_server); |
108 GURL url = g_test_server->GetURL(echo_body_path); | 255 GURL url = g_test_server->GetURL(kEchoBodyPath); |
109 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); | 256 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); |
110 } | 257 } |
111 | 258 |
112 jstring GetEchoHeaderURL(JNIEnv* env, jclass jcaller, jstring jheader) { | 259 jstring GetEchoHeaderURL(JNIEnv* env, jclass jcaller, jstring jheader) { |
113 DCHECK(g_test_server); | 260 DCHECK(g_test_server); |
114 GURL url = g_test_server->GetURL(echo_header_path); | 261 GURL url = g_test_server->GetURL(kEchoHeaderPath); |
115 GURL::Replacements replacements; | 262 GURL::Replacements replacements; |
116 std::string header = base::android::ConvertJavaStringToUTF8(env, jheader); | 263 std::string header = base::android::ConvertJavaStringToUTF8(env, jheader); |
117 replacements.SetQueryStr(header.c_str()); | 264 replacements.SetQueryStr(header.c_str()); |
118 url = url.ReplaceComponents(replacements); | 265 url = url.ReplaceComponents(replacements); |
119 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); | 266 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); |
120 } | 267 } |
121 | 268 |
122 jstring GetEchoAllHeadersURL(JNIEnv* env, jclass jcaller) { | 269 jstring GetEchoAllHeadersURL(JNIEnv* env, jclass jcaller) { |
123 DCHECK(g_test_server); | 270 DCHECK(g_test_server); |
124 GURL url = g_test_server->GetURL(echo_all_headers_path); | 271 GURL url = g_test_server->GetURL(kEchoAllHeadersPath); |
125 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); | 272 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); |
126 } | 273 } |
127 | 274 |
128 jstring GetEchoMethodURL(JNIEnv* env, jclass jcaller) { | 275 jstring GetEchoMethodURL(JNIEnv* env, jclass jcaller) { |
129 DCHECK(g_test_server); | 276 DCHECK(g_test_server); |
130 GURL url = g_test_server->GetURL(echo_method_path); | 277 GURL url = g_test_server->GetURL(kEchoMethodPath); |
131 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); | 278 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); |
132 } | 279 } |
133 | 280 |
134 jstring GetRedirectToEchoBody(JNIEnv* env, jclass jcaller) { | 281 jstring GetRedirectToEchoBody(JNIEnv* env, jclass jcaller) { |
135 DCHECK(g_test_server); | 282 DCHECK(g_test_server); |
136 GURL url = g_test_server->GetURL(redirect_to_echo_body_path); | 283 GURL url = g_test_server->GetURL(kRedirectToEchoBodyPath); |
137 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); | 284 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); |
138 } | 285 } |
139 | 286 |
140 jstring GetFileURL(JNIEnv* env, jclass jcaller, jstring jfile_path) { | 287 jstring GetFileURL(JNIEnv* env, jclass jcaller, jstring jfile_path) { |
141 DCHECK(g_test_server); | 288 DCHECK(g_test_server); |
142 std::string file = base::android::ConvertJavaStringToUTF8(env, jfile_path); | 289 std::string file = base::android::ConvertJavaStringToUTF8(env, jfile_path); |
143 GURL url = g_test_server->GetURL(file); | 290 GURL url = g_test_server->GetURL(file); |
144 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); | 291 return base::android::ConvertUTF8ToJavaString(env, url.spec()).Release(); |
145 } | 292 } |
146 | 293 |
| 294 jstring GetSdchURL(JNIEnv* env, jclass jcaller) { |
| 295 DCHECK(g_test_server); |
| 296 std::string url(base::StringPrintf("http://%s:%d", kFakeSdchDomain, |
| 297 g_test_server->port())); |
| 298 return base::android::ConvertUTF8ToJavaString(env, url).Release(); |
| 299 } |
| 300 |
147 bool RegisterNativeTestServer(JNIEnv* env) { | 301 bool RegisterNativeTestServer(JNIEnv* env) { |
148 return RegisterNativesImpl(env); | 302 return RegisterNativesImpl(env); |
149 } | 303 } |
150 | 304 |
151 } // namespace cronet | 305 } // namespace cronet |
OLD | NEW |