Files
Flyinghead 6d2befc804 http: allow req and resp headers with GET. 30 s timeout. linux tests
Add http::get function with request and response header params.
Set a 30 s connection timeout for all http requests (except on macOS).
Linux tests with postb.in
2026-03-22 15:39:51 +01:00

183 lines
7.8 KiB
Plaintext

/*
Copyright 2022 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#import <Foundation/Foundation.h>
#include "oslib/http_client.h"
#include "stdclass.h"
namespace http {
int get(const std::string& url, std::vector<u8>& content, const Headers *reqHeaders, Headers *respHeaders)
{
NSString *nsurl = [NSString stringWithCString:url.c_str()
encoding:[NSString defaultCStringEncoding]];
NSString *userAgent = [NSString stringWithCString:getUserAgent().c_str()
encoding:[NSString defaultCStringEncoding]];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:nsurl]];
[urlRequest setValue:userAgent forHTTPHeaderField:@"User-Agent"];
if (reqHeaders != nullptr)
{
for (const auto& [ name, value ] : *reqHeaders)
{
NSString *nsname = [NSString stringWithCString:name.c_str()
encoding:[NSString defaultCStringEncoding]];
NSString *nsvalue = [NSString stringWithCString:value.c_str()
encoding:[NSString defaultCStringEncoding]];
[urlRequest setValue:nsvalue forHTTPHeaderField:nsname];
}
}
// TODO can't set a connection timeout apparently. Need to switch to asynchronous API
// but default timeout might be close to 30 s...
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
if (error != nil)
return 500;
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (respHeaders != nullptr)
{
NSDictionary *headers = httpResponse.allHeaderFields;
NSEnumerator *enumerator = [headers keyEnumerator];
id key;
while ((key = [enumerator nextObject]))
{
std::string strkey([key UTF8String]);
string_tolower(strkey);
std::string strvalue([[headers objectForKey:key] UTF8String]);
respHeaders->emplace_back(strkey, strvalue);
}
}
content.clear();
content.insert(content.begin(), (const u8 *)[data bytes], (const u8 *)[data bytes] + [data length]);
return [httpResponse statusCode];
}
int post(const std::string& url, const char *payload, const char *contentType, std::vector<u8>& reply)
{
NSString *nsurl = [NSString stringWithCString:url.c_str()
encoding:[NSString defaultCStringEncoding]];
NSString *userAgent = [NSString stringWithCString:getUserAgent().c_str()
encoding:[NSString defaultCStringEncoding]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:nsurl]];
[request setHTTPMethod:@"POST"];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setHTTPShouldHandleCookies:NO];
size_t payloadSize = strlen(payload);
[request setHTTPBody:[NSData dataWithBytes:payload length:payloadSize]];
NSString *postLength = [NSString stringWithFormat:@"%ld", (unsigned long)payloadSize];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
NSString *nscontentType = contentType != nullptr ? [NSString stringWithCString:contentType
encoding:[NSString defaultCStringEncoding]]
: @"application/x-www-form-urlencoded";
[request setValue:nscontentType forHTTPHeaderField:@"Content-Type"];
[request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error != nil)
return 500;
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
reply.clear();
reply.insert(reply.begin(), (const u8 *)[data bytes], (const u8 *)[data bytes] + [data length]);
return [httpResponse statusCode];
}
int post(const std::string& url, const std::vector<PostField>& fields)
{
NSString *nsurl = [NSString stringWithCString:url.c_str()
encoding:[NSString defaultCStringEncoding]];
NSString *userAgent = [NSString stringWithCString:getUserAgent().c_str()
encoding:[NSString defaultCStringEncoding]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:nsurl]];
[request setHTTPMethod:@"POST"];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setHTTPShouldHandleCookies:NO];
NSString *boundary = @"----flycast-boundary-7192397596";
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request setValue:contentType forHTTPHeaderField: @"Content-Type"];
[request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
NSMutableData *body = [NSMutableData data];
for (const PostField& field : fields)
{
NSString *value = [NSString stringWithCString:field.value.c_str()
encoding:[NSString defaultCStringEncoding]];
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"", [NSString stringWithCString:field.name.c_str()
encoding:[NSString defaultCStringEncoding]]] dataUsingEncoding:NSUTF8StringEncoding]];
if (!field.contentType.empty())
{
[body appendData:[[NSString stringWithFormat:@"; filename=\"%@\"\r\n", value] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Type: %@", [NSString stringWithCString:field.contentType.c_str()
encoding:[NSString defaultCStringEncoding]]] dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[@"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
if (field.contentType.empty())
{
[body appendData:[value dataUsingEncoding:NSUTF8StringEncoding]];
}
else
{
NSError* error = nil;
NSData *filedata = [NSData dataWithContentsOfFile:value options:0 error:&error];
if (error != nil)
return 500;
[body appendData:filedata];
}
[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:body];
NSString *postLength = [NSString stringWithFormat:@"%ld", (unsigned long)[body length]];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
NSURLResponse *response = nil;
NSError *error = nil;
[NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error != nil)
return 500;
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
return [httpResponse statusCode];
}
void init() {
}
void term() {
}
}