blob: 58330a1b220c05eafbd44c59d979c72819b3055f [file] [log] [blame]
jat@google.com64a55cb2009-10-16 14:16:57 +00001/*
2 * Copyright 2006 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17// A very slim and portable set of JNI methods for GWT.
18
19#include <jni.h>
20#include <windows.h>
21#include "wininet.h"
22
23// A home-brewed vector class to eliminate dependence on STL.
24// Reuse at your own peril, it hasn't been tested to do anything
25// more than we're actually using it for here.
26struct char_vector {
27 char_vector(long initSize=0): sz(0), cap(0), buf(0) {
28 resize(initSize);
29 }
30
31 ~char_vector() {
32 clear();
33 }
34
35 char& operator[](long i) {
36 return buf[i];
37 }
38
39 void append(const char* start, const char* end) {
40 long len = end - start;
41 long cursize = sz;
42 resize(sz + len);
43 memcpy(&buf[cursize], start, len);
44 }
45
46 void clear() {
47 if (buf)
48 free(buf);
49 buf = 0;
50 sz = cap = 0;
51 }
52
53 void reserve(long newcap) {
54 if (newcap <= cap)
55 return;
56 char* newbuf = (char*)malloc(newcap);
57 if (!newbuf)
58 return;
59 if (buf) {
60 memcpy(newbuf, buf, sz);
61 free(buf);
62 }
63 buf = newbuf;
64 cap = newcap;
65 }
66
67 void resize(long newsize) {
68 reserve(newsize);
69 sz = newsize;
70 }
71
72 long size() {
73 return sz;
74 }
75
76private:
77 long sz;
78 long cap;
79 char* buf;
80};
81
82// Does an HTTP GET on the specified URL using the WinInet subsystem, which respects proxy settings.
83// Returns a status code such that
84// 0 Success
85// < 0 Failure at some step (see the code below)
86// > 0 Windows system error code
87//
88int fetch(const char* userAgent, const char* url, char_vector& response) {
89 int status = -1;
90 HINTERNET h1 = InternetOpenA(userAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
91 if (h1 != NULL) {
92 status = -2;
93 DWORD flags = INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI;
94 HINTERNET h2 = InternetOpenUrlA(h1, url, "", -1L, flags, 0);
95 if (h2 != NULL) {
96 status = -3;
97 const int MAX_BUFFER = 1024 * 1024; // don't grow past this ever
98 bool notDone = true;
99 response.clear();
100 response.reserve(4096);
101 char_vector chunk(4096); // initial buffer size
102 while (notDone && chunk.size() < MAX_BUFFER) {
103 DWORD toRead = (DWORD)chunk.size();
104 DWORD didRead;
105 while (true) {
106 if (InternetReadFile(h2, (LPVOID)&chunk[0], toRead, &didRead)) {
107 if (didRead != 0) {
108 // Copy to response.
109 //
110 response.append(&chunk[0], &chunk[0] + didRead);
111
112 // Keep going.
113 //
114 } else {
115 // EOF.
116 //
117 status = 0;
118 notDone = false;
119 break;
120 }
121 } else {
122 // Oops -- why?
123 //
124 DWORD err = GetLastError();
125 if (err == ERROR_INSUFFICIENT_BUFFER) {
126 // Grow the buffer and retry.
127 //
128 chunk.resize(toRead * 2);
129 break;
130 } else {
131 // Give up.
132 //
133 status = err;
134 notDone = false;
135 break;
136 }
137 }
138 }
139 }
140 InternetCloseHandle(h2);
141 }
142 InternetCloseHandle(h1);
143 }
144 return status;
145}
146
147extern "C" {
148
149class UTFChars {
150public:
151 UTFChars(JNIEnv* env, jstring str): m_env(env), m_str(str) {
152 m_ptr = env->GetStringUTFChars(str, NULL);
153 }
154
155 ~UTFChars() {
156 if (m_ptr) {
157 m_env->ReleaseStringUTFChars(m_str, m_ptr);
158 }
159 }
160
161 operator const char*() const {
162 return m_ptr;
163 }
164
165 JNIEnv* m_env;
166 jstring m_str;
167 const char* m_ptr;
168};
169
170/*
171 * Class: com_google_gwt_dev_shell_ie_LowLevelIE6
172 * Method: _httpGet
173 * Signature: (Ljava/lang/String;Ljava/lang/String;[[B)I
174 */
175JNIEXPORT jint JNICALL Java_com_google_gwt_dev_shell_ie_LowLevelIE6__1httpGet(JNIEnv* env, jclass, jstring userAgent, jstring url, jobjectArray out) {
176 UTFChars userAgentChars(env, userAgent);
177 if (!userAgentChars || env->ExceptionCheck()) {
178 return -100;
179 }
180
181 UTFChars urlChars(env, url);
182 if (!urlChars || env->ExceptionCheck()) {
183 return -101;
184 }
185
186 // Do the fetch.
187 //
188 char_vector response;
189 int status = fetch(userAgentChars, urlChars, response);
190
191 if (status != 0) {
192 // Failure.
193 //
194 return status;
195 }
196
197 // Copy the response.
198 //
199 jbyteArray byteArray = env->NewByteArray(response.size());
200 if (!byteArray || env->ExceptionCheck()) {
201 return -102;
202 }
203
204 jbyte* bytes = env->GetByteArrayElements(byteArray, NULL);
205 if (!bytes || env->ExceptionCheck()) {
206 return -103;
207 }
208
209 memcpy(bytes, &response[0], response.size());
210
211 env->ReleaseByteArrayElements(byteArray, bytes, 0);
212
213 // May throw immediately after return if out array is bad.
214 //
215 env->SetObjectArrayElement(out, 0, byteArray);
216
217 return 0;
218}
219
220} // extern "C"