session-ios/Signal/src/network/http/HttpRequest.m

164 lines
6.1 KiB
Objective-C

#import "HttpRequest.h"
#import "Util.h"
#import "CryptoTools.h"
#import "HttpRequestOrResponse.h"
@interface HttpRequest ()
@property NSString* method;
@property NSString* location;
@property NSString* optionalBody;
@property NSDictionary* headers;
@end
@implementation HttpRequest
+(HttpRequest*)httpRequestWithMethod:(NSString*)method andLocation:(NSString*)location andHeaders:(NSDictionary*)headers andOptionalBody:(NSString*)optionalBody {
require(method != nil);
require(location != nil);
require(headers != nil);
require((optionalBody == nil) == (headers[@"Content-Length"] == nil));
require(optionalBody == nil || [[@(optionalBody.length) description] isEqualToString:headers[@"Content-Length"]]);
HttpRequest* s = [HttpRequest new];
s->_method = method;
s->_location = location;
s->_optionalBody = optionalBody;
s->_headers = headers;
return s;
}
+(HttpRequest*)httpRequestUnauthenticatedWithMethod:(NSString*)method
andLocation:(NSString*)location
andOptionalBody:(NSString*)optionalBody {
require(method != nil);
require(location != nil);
NSMutableDictionary* headers = [NSMutableDictionary dictionary];
if (optionalBody != nil) {
headers[@"Content-Length"] = [@(optionalBody.length) stringValue];
}
HttpRequest* s = [HttpRequest new];
s->_method = method;
s->_location = location;
s->_optionalBody = optionalBody;
s->_headers = headers;
return s;
}
+(HttpRequest*)httpRequestWithBasicAuthenticationAndMethod:(NSString*)method
andLocation:(NSString*)location
andOptionalBody:(NSString*)optionalBody
andLocalNumber:(PhoneNumber*)localNumber
andPassword:(NSString*)password {
require(method != nil);
require(location != nil);
require(password != nil);
require(localNumber != nil);
NSMutableDictionary* headers = [NSMutableDictionary dictionary];
if (optionalBody != nil) {
headers[@"Content-Length"] = [@(optionalBody.length) stringValue];
}
headers[@"Authorization"] = [HttpRequest computeBasicAuthorizationTokenForLocalNumber:localNumber andPassword:password];
HttpRequest* s = [HttpRequest new];
s->_method = method;
s->_location = location;
s->_optionalBody = optionalBody;
s->_headers = headers;
return s;
}
+(HttpRequest*)httpRequestWithOtpAuthenticationAndMethod:(NSString*)method
andLocation:(NSString*)location
andOptionalBody:(NSString*)optionalBody
andLocalNumber:(PhoneNumber*)localNumber
andPassword:(NSString*)password
andCounter:(int64_t)counter {
require(method != nil);
require(location != nil);
require(password != nil);
NSMutableDictionary* headers = [NSMutableDictionary dictionary];
if (optionalBody != nil) {
headers[@"Content-Length"] = [@(optionalBody.length) stringValue];
}
headers[@"Authorization"] = [HttpRequest computeOtpAuthorizationTokenForLocalNumber:localNumber andCounterValue:counter andPassword:password];
HttpRequest* s = [HttpRequest new];
s->_method = method;
s->_location = location;
s->_optionalBody = optionalBody;
s->_headers = headers;
return s;
}
+(HttpRequest*) httpRequestFromData:(NSData*)data {
require(data != nil);
NSUInteger requestSize;
HttpRequestOrResponse* http = [HttpRequestOrResponse tryExtractFromPartialData:data usedLengthOut:&requestSize];
checkOperation(http.isRequest && requestSize == data.length);
return [http request];
}
+(NSString*) computeOtpAuthorizationTokenForLocalNumber:(PhoneNumber*)localNumber
andCounterValue:(int64_t)counterValue
andPassword:(NSString*)password {
require(localNumber != nil);
require(password != nil);
NSString* rawToken = [NSString stringWithFormat:@"%@:%@:%lld",
localNumber.toE164,
[CryptoTools computeOtpWithPassword:password andCounter:counterValue],
counterValue];
return [@"OTP " stringByAppendingString:rawToken.encodedAsUtf8.encodedAsBase64];
}
+(NSString*) computeBasicAuthorizationTokenForLocalNumber:(PhoneNumber*)localNumber andPassword:(NSString*)password {
NSString* rawToken = [NSString stringWithFormat:@"%@:%@",
localNumber.toE164,
password];
return [@"Basic " stringByAppendingString:rawToken.encodedAsUtf8.encodedAsBase64];
}
-(NSString*) toHttp {
NSMutableArray* r = [NSMutableArray array];
[r addObject:self.method];
[r addObject:@" "];
[r addObject:self.location];
[r addObject:@" HTTP/1.0\r\n"];
for (NSString* key in self.headers) {
[r addObject:key];
[r addObject:@": "];
[r addObject:(self.headers)[key]];
[r addObject:@"\r\n"];
}
[r addObject:@"\r\n"];
if (self.optionalBody != nil) [r addObject:self.optionalBody];
return [r componentsJoinedByString:@""];
}
-(NSData*) serialize {
return self.toHttp.encodedAsUtf8;
}
-(bool) isEqualToHttpRequest:(HttpRequest *)other {
return [self.toHttp isEqualToString:other.toHttp]
&& [self.method isEqualToString:other.method]
&& [self.location isEqualToString:other.location]
&& (self.optionalBody == other.optionalBody || [self.optionalBody isEqualToString:[other optionalBody]])
&& [self.headers isEqualToDictionary:other.headers];
}
-(NSString*) description {
return [NSString stringWithFormat:@"%@ %@%@",
self.method,
self.location,
self.optionalBody == nil ? @""
: self.optionalBody.length == 0 ? @" [empty body]"
: @" [...body...]"];
}
@end