1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-23 01:44:22 +00:00

Added http status code access for URL::createInputStream(). Also added threading + header display to the demo's http page.

This commit is contained in:
jules 2014-03-06 22:26:58 +00:00
parent 14a5fcf410
commit 4889822eaf
11 changed files with 205 additions and 132 deletions

View file

@ -589,10 +589,34 @@ public final class JuceDemo extends Activity
//==============================================================================
public static class HTTPStream
{
public HTTPStream (HttpURLConnection connection_) throws IOException
public HTTPStream (HttpURLConnection connection_,
int[] statusCode, StringBuffer responseHeaders) throws IOException
{
connection = connection_;
inputStream = new BufferedInputStream (connection.getInputStream());
try
{
inputStream = new BufferedInputStream (connection.getInputStream());
}
catch (IOException e)
{
if (connection.getResponseCode() < org.apache.http.HttpStatus.SC_BAD_REQUEST)
throw e;
}
finally
{
statusCode[0] = connection.getResponseCode();
}
if (statusCode[0] >= org.apache.http.HttpStatus.SC_BAD_REQUEST)
inputStream = connection.getErrorStream();
else
inputStream = connection.getInputStream();
for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet())
if (entry.getKey() != null && entry.getValue() != null)
responseHeaders.append (entry.getKey() + ": "
+ android.text.TextUtils.join (",", entry.getValue()) + "\n");
}
public final void release()
@ -634,30 +658,31 @@ public final class JuceDemo extends Activity
private long position;
}
public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData,
String headers, int timeOutMs,
java.lang.StringBuffer responseHeaders)
public static final HTTPStream createHTTPStream (String address,
boolean isPost, byte[] postData, String headers,
int timeOutMs, int[] statusCode,
StringBuffer responseHeaders)
{
try
{
HttpURLConnection connection = (HttpURLConnection) (new URL (address).openConnection());
HttpURLConnection connection = (HttpURLConnection) (new URL(address)
.openConnection());
if (connection != null)
{
try
{
if (isPost)
{
connection.setConnectTimeout (timeOutMs);
connection.setDoOutput (true);
connection.setChunkedStreamingMode (0);
connection.setRequestMethod("POST");
connection.setConnectTimeout(timeOutMs);
connection.setDoOutput(true);
connection.setChunkedStreamingMode(0);
OutputStream out = connection.getOutputStream();
out.write (postData);
out.write(postData);
out.flush();
}
return new HTTPStream (connection);
return new HTTPStream (connection, statusCode, responseHeaders);
}
catch (Throwable e)
{
@ -665,8 +690,7 @@ public final class JuceDemo extends Activity
}
}
}
catch (Throwable e)
{}
catch (Throwable e) {}
return null;
}

View file

@ -28,11 +28,13 @@
//==============================================================================
class NetworkingDemo : public Component,
private Button::Listener
private Button::Listener,
private Thread
{
public:
NetworkingDemo()
: resultsBox (resultsDocument, nullptr)
: Thread ("Network Demo"),
resultsBox (resultsDocument, nullptr)
{
setOpaque (true);
@ -69,6 +71,37 @@ public:
resultsBox.setBounds (area.reduced (8));
}
void run() override
{
String result (getResultText (urlBox.getText()));
MessageManagerLock mml (this);
if (mml.lockWasGained())
resultsBox.loadContent (result);
}
String getResultText (const URL& url)
{
StringPairArray responseHeaders;
int statusCode = 0;
ScopedPointer<InputStream> stream (url.createInputStream (false, nullptr, nullptr, String(),
10000, // timeout in millisecs
&responseHeaders, &statusCode));
if (stream != nullptr)
return (statusCode != 0 ? "Status code: " + String (statusCode) + newLine : String())
+ "Response headers: " + newLine
+ responseHeaders.getDescription() + newLine
+ "----------------------------------------------------" + newLine
+ stream->readEntireStreamAsString();
if (statusCode != 0)
return "Failed to connect, status code = " + String (statusCode);
return "Failed to connect!";
}
private:
TextEditor urlBox;
TextButton fetchButton;
@ -76,16 +109,10 @@ private:
CodeDocument resultsDocument;
CodeEditorComponent resultsBox;
void downloadUrl()
{
URL url (urlBox.getText());
resultsBox.loadContent (url.readEntireTextStream());
}
void buttonClicked (Button* button) override
{
if (button == &fetchButton)
downloadUrl();
startThread();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NetworkingDemo)

View file

@ -133,7 +133,6 @@ namespace juce
#include "network/juce_MACAddress.cpp"
#include "network/juce_NamedPipe.cpp"
#include "network/juce_Socket.cpp"
#include "network/juce_URL.cpp"
#include "network/juce_IPAddress.cpp"
#include "streams/juce_BufferedInputStream.cpp"
#include "streams/juce_FileInputSource.cpp"
@ -219,5 +218,6 @@ namespace juce
#include "threads/juce_ChildProcess.cpp"
#include "threads/juce_HighResolutionTimer.cpp"
#include "network/juce_URL.cpp"
}

View file

@ -589,10 +589,34 @@ public final class JuceAppActivity extends Activity
//==============================================================================
public static class HTTPStream
{
public HTTPStream (HttpURLConnection connection_) throws IOException
public HTTPStream (HttpURLConnection connection_,
int[] statusCode, StringBuffer responseHeaders) throws IOException
{
connection = connection_;
inputStream = new BufferedInputStream (connection.getInputStream());
try
{
inputStream = new BufferedInputStream (connection.getInputStream());
}
catch (IOException e)
{
if (connection.getResponseCode() < org.apache.http.HttpStatus.SC_BAD_REQUEST)
throw e;
}
finally
{
statusCode[0] = connection.getResponseCode();
}
if (statusCode[0] >= org.apache.http.HttpStatus.SC_BAD_REQUEST)
inputStream = connection.getErrorStream();
else
inputStream = connection.getInputStream();
for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet())
if (entry.getKey() != null && entry.getValue() != null)
responseHeaders.append (entry.getKey() + ": "
+ android.text.TextUtils.join (",", entry.getValue()) + "\n");
}
public final void release()
@ -634,30 +658,31 @@ public final class JuceAppActivity extends Activity
private long position;
}
public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData,
String headers, int timeOutMs,
java.lang.StringBuffer responseHeaders)
public static final HTTPStream createHTTPStream (String address,
boolean isPost, byte[] postData, String headers,
int timeOutMs, int[] statusCode,
StringBuffer responseHeaders)
{
try
{
HttpURLConnection connection = (HttpURLConnection) (new URL (address).openConnection());
HttpURLConnection connection = (HttpURLConnection) (new URL(address)
.openConnection());
if (connection != null)
{
try
{
if (isPost)
{
connection.setConnectTimeout (timeOutMs);
connection.setDoOutput (true);
connection.setChunkedStreamingMode (0);
connection.setRequestMethod("POST");
connection.setConnectTimeout(timeOutMs);
connection.setDoOutput(true);
connection.setChunkedStreamingMode(0);
OutputStream out = connection.getOutputStream();
out.write (postData);
out.write(postData);
out.flush();
}
return new HTTPStream (connection);
return new HTTPStream (connection, statusCode, responseHeaders);
}
catch (Throwable e)
{
@ -665,8 +690,7 @@ public final class JuceAppActivity extends Activity
}
}
}
catch (Throwable e)
{}
catch (Throwable e) {}
return null;
}

View file

@ -365,7 +365,7 @@ extern ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder;
METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \
METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \
METHOD (renderGlyph, "renderGlyph", "(CLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;ILjava/lang/StringBuffer;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \
METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \
METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \

View file

@ -71,6 +71,7 @@ public:
WebInputStream (String address, bool isPost, const MemoryBlock& postData,
URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
const String& headers, int timeOutMs, StringPairArray* responseHeaders)
: statusCode (0)
{
if (! address.contains ("://"))
address = "http://" + address;
@ -91,6 +92,9 @@ public:
// thread. You'll need to move your networking code to a background thread to keep it happy..
jassert (Thread::getCurrentThread() != nullptr);
jintArray statusCodeArray = env->NewIntArray (1);
jassert (statusCodeArray != 0);
stream = GlobalRef (env->CallStaticObjectMethod (JuceAppActivity,
JuceAppActivity.createHTTPStream,
javaString (address).get(),
@ -98,8 +102,14 @@ public:
postDataArray,
javaString (headers).get(),
(jint) timeOutMs,
statusCodeArray,
responseHeaderBuffer.get()));
jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, 0);
statusCode = statusCodeElements[0];
env->ReleaseIntArrayElements (statusCodeArray, statusCodeElements, 0);
env->DeleteLocalRef (statusCodeArray);
if (postDataArray != 0)
env->DeleteLocalRef (postDataArray);
@ -135,6 +145,8 @@ public:
}
//==============================================================================
bool isError() const { return stream == nullptr; }
bool isExhausted() override { return stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted); }
int64 getTotalLength() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0; }
int64 getPosition() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0; }
@ -162,18 +174,8 @@ public:
//==============================================================================
GlobalRef stream;
int statusCode;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream)
};
InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData,
OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
const String& headers, const int timeOutMs, StringPairArray* responseHeaders)
{
ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData,
progressCallback, progressCallbackContext,
headers, timeOutMs, responseHeaders));
return wi->stream != 0 ? wi.release() : nullptr;
}

View file

@ -72,11 +72,11 @@ public:
WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_,
URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
const String& headers_, int timeOutMs_, StringPairArray* responseHeaders)
: socketHandle (-1), levelsOfRedirection (0),
: statusCode (0), socketHandle (-1), levelsOfRedirection (0),
address (address_), headers (headers_), postData (postData_), position (0),
finished (false), isPost (isPost_), timeOutMs (timeOutMs_)
{
createConnection (progressCallback, progressCallbackContext);
statusCode = createConnection (progressCallback, progressCallbackContext);
if (responseHeaders != nullptr && ! isError())
{
@ -144,7 +144,7 @@ public:
{
closeSocket();
position = 0;
createConnection (0, 0);
statusCode = createConnection (0, 0);
}
skipNextBytes (wantedPos - position);
@ -154,6 +154,8 @@ public:
}
//==============================================================================
int statusCode;
private:
int socketHandle, levelsOfRedirection;
StringArray headerLines;
@ -173,7 +175,7 @@ private:
levelsOfRedirection = 0;
}
void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext)
int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext)
{
closeSocket();
@ -189,7 +191,7 @@ private:
String hostName, hostPath;
int hostPort;
if (! decomposeURL (address, hostName, hostPath, hostPort))
return;
return 0;
String serverName, proxyName, proxyPath;
int proxyPort = 0;
@ -199,7 +201,7 @@ private:
if (proxyURL.startsWithIgnoreCase ("http://"))
{
if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort))
return;
return 0;
serverName = proxyName;
port = proxyPort;
@ -219,14 +221,14 @@ private:
struct addrinfo* result = nullptr;
if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == 0)
return;
return 0;
socketHandle = socket (result->ai_family, result->ai_socktype, 0);
if (socketHandle == -1)
{
freeaddrinfo (result);
return;
return 0;
}
int receiveBufferSize = 16384;
@ -241,7 +243,7 @@ private:
{
closeSocket();
freeaddrinfo (result);
return;
return 0;
}
freeaddrinfo (result);
@ -254,7 +256,7 @@ private:
progressCallback, progressCallbackContext))
{
closeSocket();
return;
return 0;
}
}
@ -265,15 +267,15 @@ private:
{
headerLines = StringArray::fromLines (responseHeader);
const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false)
.substring (0, 3).getIntValue();
const int status = responseHeader.fromFirstOccurrenceOf (" ", false, false)
.substring (0, 3).getIntValue();
//int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue();
//bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked");
String location (findHeaderItem (headerLines, "Location:"));
if (statusCode >= 300 && statusCode < 400
if (status >= 300 && status < 400
&& location.isNotEmpty() && location != address)
{
if (! location.startsWithIgnoreCase ("http://"))
@ -282,18 +284,18 @@ private:
if (++levelsOfRedirection <= 3)
{
address = location;
createConnection (progressCallback, progressCallbackContext);
return;
return createConnection (progressCallback, progressCallbackContext);
}
}
else
{
levelsOfRedirection = 0;
return;
return status;
}
}
closeSocket();
return 0;
}
//==============================================================================
@ -437,14 +439,3 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream)
};
InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData,
OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
const String& headers, const int timeOutMs, StringPairArray* responseHeaders)
{
ScopedPointer<WebInputStream> wi (new WebInputStream (address, isPost, postData,
progressCallback, progressCallbackContext,
headers, timeOutMs, responseHeaders));
return wi->isError() ? nullptr : wi.release();
}

View file

@ -118,6 +118,7 @@ public:
connection (nil),
data ([[NSMutableData data] retain]),
headers (nil),
statusCode (0),
initialised (false),
hasFailed (false),
hasFinished (false)
@ -202,7 +203,11 @@ public:
headers = nil;
if ([response isKindOfClass: [NSHTTPURLResponse class]])
headers = [[((NSHTTPURLResponse*) response) allHeaderFields] retain];
{
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
headers = [[httpResponse allHeaderFields] retain];
statusCode = (int) [httpResponse statusCode];
}
}
void didFailWithError (NSError* error)
@ -251,6 +256,7 @@ public:
NSURLConnection* connection;
NSMutableData* data;
NSDictionary* headers;
int statusCode;
bool initialised, hasFailed, hasFinished;
private:
@ -318,7 +324,7 @@ public:
WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_,
URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
const String& headers_, int timeOutMs_, StringPairArray* responseHeaders)
: address (address_), headers (headers_), postData (postData_), position (0),
: statusCode (0), address (address_), headers (headers_), postData (postData_), position (0),
finished (false), isPost (isPost_), timeOutMs (timeOutMs_)
{
JUCE_AUTORELEASEPOOL
@ -327,6 +333,8 @@ public:
if (responseHeaders != nullptr && connection != nullptr && connection->headers != nil)
{
statusCode = connection->statusCode;
NSEnumerator* enumerator = [connection->headers keyEnumerator];
while (NSString* key = [enumerator nextObject])
@ -380,6 +388,8 @@ public:
return true;
}
int statusCode;
private:
ScopedPointer<URLConnectionState> connection;
String address, headers;
@ -428,14 +438,3 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream)
};
InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData,
OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
const String& headers, const int timeOutMs, StringPairArray* responseHeaders)
{
ScopedPointer<WebInputStream> wi (new WebInputStream (address, isPost, postData,
progressCallback, progressCallbackContext,
headers, timeOutMs, responseHeaders));
return wi->isError() ? nullptr : wi.release();
}

View file

@ -41,41 +41,50 @@ public:
WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_,
URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
const String& headers_, int timeOutMs_, StringPairArray* responseHeaders)
: connection (0), request (0),
: statusCode (0), connection (0), request (0),
address (address_), headers (headers_), postData (postData_), position (0),
finished (false), isPost (isPost_), timeOutMs (timeOutMs_)
{
createConnection (progressCallback, progressCallbackContext);
if (responseHeaders != nullptr && ! isError())
if (! isError())
{
DWORD bufferSizeBytes = 4096;
for (;;)
if (responseHeaders != nullptr)
{
HeapBlock<char> buffer ((size_t) bufferSizeBytes);
DWORD bufferSizeBytes = 4096;
if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0))
for (;;)
{
StringArray headersArray;
headersArray.addLines (String (reinterpret_cast<const WCHAR*> (buffer.getData())));
HeapBlock<char> buffer ((size_t) bufferSizeBytes);
for (int i = 0; i < headersArray.size(); ++i)
if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0))
{
const String& header = headersArray[i];
const String key (header.upToFirstOccurrenceOf (": ", false, false));
const String value (header.fromFirstOccurrenceOf (": ", false, false));
const String previousValue ((*responseHeaders) [key]);
StringArray headersArray;
headersArray.addLines (String (reinterpret_cast<const WCHAR*> (buffer.getData())));
responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value));
for (int i = 0; i < headersArray.size(); ++i)
{
const String& header = headersArray[i];
const String key (header.upToFirstOccurrenceOf (": ", false, false));
const String value (header.fromFirstOccurrenceOf (": ", false, false));
const String previousValue ((*responseHeaders) [key]);
responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value));
}
break;
}
break;
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break;
}
DWORD status = 0;
DWORD statusSize = sizeof (status);
if (HttpQueryInfo (request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &statusSize, 0))
statusCode = (int) status;
}
}
@ -145,6 +154,8 @@ public:
return true;
}
int statusCode;
private:
//==============================================================================
HINTERNET connection, request;
@ -305,17 +316,6 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream)
};
InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData,
OpenStreamProgressCallback* progressCallback, void* progressCallbackContext,
const String& headers, const int timeOutMs, StringPairArray* responseHeaders)
{
ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData,
progressCallback, progressCallbackContext,
headers, timeOutMs, responseHeaders));
return wi->isError() ? nullptr : wi.release();
}
//==============================================================================
struct GetAdaptersInfoHelper

View file

@ -330,7 +330,8 @@ InputStream* URL::createInputStream (const bool usePostCommand,
void* const progressCallbackContext,
String headers,
const int timeOutMs,
StringPairArray* const responseHeaders) const
StringPairArray* const responseHeaders,
int* statusCode) const
{
MemoryBlock headersAndPostData;
@ -343,9 +344,15 @@ InputStream* URL::createInputStream (const bool usePostCommand,
if (! headers.endsWithChar ('\n'))
headers << "\r\n";
return createNativeStream (toString (! usePostCommand), usePostCommand, headersAndPostData,
progressCallback, progressCallbackContext,
headers, timeOutMs, responseHeaders);
ScopedPointer<WebInputStream> wi (new WebInputStream (toString (! usePostCommand),
usePostCommand, headersAndPostData,
progressCallback, progressCallbackContext,
headers, timeOutMs, responseHeaders));
if (statusCode != nullptr)
*statusCode = wi->statusCode;
return wi->isError() ? nullptr : wi.release();
}
//==============================================================================

View file

@ -247,8 +247,10 @@ public:
@param connectionTimeOutMs if 0, this will use whatever default setting the OS chooses. If
a negative number, it will be infinite. Otherwise it specifies a
time in milliseconds.
@param responseHeaders if this is non-zero, all the (key, value) pairs received as headers
@param responseHeaders if this is non-null, all the (key, value) pairs received as headers
in the response will be stored in this array
@param statusCode if this is non-null, it will get set to the http status code, if one
is known, or 0 if a code isn't available
@returns an input stream that the caller must delete, or a null pointer if there was an
error trying to open it.
*/
@ -257,7 +259,8 @@ public:
void* progressCallbackContext = nullptr,
String extraHeaders = String(),
int connectionTimeOutMs = 0,
StringPairArray* responseHeaders = nullptr) const;
StringPairArray* responseHeaders = nullptr,
int* statusCode = nullptr) const;
//==============================================================================
@ -345,10 +348,6 @@ private:
URL (const String&, int);
void addParameter (const String&, const String&);
static InputStream* createNativeStream (const String& address, bool isPost, const MemoryBlock& postData,
OpenStreamProgressCallback* progressCallback,
void* progressCallbackContext, const String& headers,
const int timeOutMs, StringPairArray* responseHeaders);
JUCE_LEAK_DETECTOR (URL)
};