GithubHelp home page GithubHelp logo

biasedbit / bbhttp Goto Github PK

View Code? Open in Web Editor NEW
193.0 13.0 21.0 7.84 MB

A modern HTTP client framework for iOS/OSX built on top of libcurl.

License: Other

Ruby 0.31% Objective-C 31.62% Shell 0.14% C 67.93%

bbhttp's Introduction

BBHTTP

BBHTTP is a rich wrapper for libcurl written in Objective-C.

It is an ARC-only library that uses features introduced by Clang 3.1. Thus, it is only suitable for iOS 5+ and OSX 10.7+.

It boasts an extremely simple and compact interface that allows you to reduce your code to fire off HTTP requests down to a couple of clean lines, while preserving full flexibility should you ever need it.

[[BBHTTPRequest readResource:@"http://foo.bar/baz"] execute:^(BBHTTPResponse* r) {
     NSLog(@"Finished: %u %@ -- received %u bytes of '%@'.",
           r.code, r.message, r.contentSize, r[@"Content-Type"]);
 } error:^(NSError* e) {
     NSLog(@"Request failed: %@", [e localizedDescription]);
 }];

// Finished: 200 OK -- received 68364 bytes of 'text/html'.

At this stage there are still a lot of rough edges to polish, bugs to fix and features missing to bring it up-to-par with other similar projects. I want to add those over time but help is always more than welcome so be sure to open issues for the features you'd love to see or drop me a mention @biasedbit on Twitter.

The API is very likely to keep mutating until this reaches 1.0.

Highlights

  • Concise asynchronous-driven usage:

    [[BBHTTPRequest deleteResource:@"http://foo.bar/baz/1"] execute:^(BBHTTPResponse* r) {
        // handle response
    } error:nil]];

    You don't even need to keep references to the requests, just fire and forget.

  • Handy common usage patterns:

    [[BBHTTPRequest readResource:@"http://foo.bar/baz/1"] setup:^(id request) {
        // Prepare request...
    } execute:^(BBHTTPResponse* response) {
        // Handle response...
    } error:^(NSError* error) {
        // Handle error...
    } finally:^{
        // Do after error OR success.
    }];
    
    
  • Get JSON effortlessly:

    [[[BBHTTPRequest readResource:@"http://foo.bar/baz.json"] asJSON] execute:^(BBHTTPResponse* r) {
        NSLog(@"User email: %@", r.content[@"user.email"]);
        NSLog(@"# of followers: %@", r.content[@"user.followers.@count"]);
    } error:^(NSError* error) {
        // Handle request *or* JSON decoding error
    }];

    Notice the keyed subscript operator behaves as valueForKeyPath: rather than valueForKey:. That's because JSON responses that would yield a NSDictionary get wrapped by BBJSONDictionary. Read more about the collection operators here.

  • Images too:

    [[BBHTTPRequest readResource:@"http://foo.bar/baz.png"] setup:^(id request) {
        [request downloadContentAsImage];
    } execute:^(BBHTTPResponse* response) {
        UIImage* image = response.content; // NSImage on OSX
        NSLog(@"image size: %@", NSStringFromCGSize(image.size));
    } error:nil];

    This example uses downloadContentAsImage on the setup block to setup the image download & conversion but you could also use the fluent syntax alternative (asImage), just like on the JSON example above.

  • Stream uploads from a NSInputStream or directly from a file:

    [[BBHTTPRequest createResource:@"http://foo.bar/baz" withContentsOfFile:@"/path/to/file"]
     setup:^(BBHTTPRequest* request) {
         request[@"Extra-Header"] = @"something else";
     } execute:^(BBHTTPResponse* response) {
         // handle response
     } error:nil];

    The request's content type and content length headers will be automatically set based on the file's properties.

  • Download to memory buffers or stream directly to file/NSOutputStream:

    [[BBHTTPRequest readResource:@"http://foo.bar/baz"] setup:^(BBHTTPRequest* request) {
        [request downloadToFile:@"/path/to/file"];
    } execute:^(BBHTTPResponse* response) {
        // handle response
    } error:nil];

    No need to delete the file if the download fails midway; BBHTTP will take care of keeping everything clean.

  • A power-dev API when you need that extra bit of control:

    BBHTTPExecutor* twitterExecutor = [BBHTTPExecutor initWithId:@"twitter.com"];
    BBHTTPExecutor* facebookExecutor = [BBHTTPExecutor initWithId:@"facebook.com"];
    twitterExecutor.maxParallelRequests = 10;
    facebookExecutor.maxParallelRequests = 2;
    ...
    BBHTTPRequest* request = [[BBHTTPRequest alloc]
                              initWithURL:[NSURL URLWithString:@"http://twitter.com/resource"]
                              andVerb:@"GET"];
    
    request[@"Accept-Language"] = @"en-us";
    request.downloadProgressBlock = ^(NSUInteger current, NSUInteger total) { /* ... */ };
    request.finishBlock = ^(BBHTTPRequest* request) { /* ... */ };
    
    [twitterExecutor executeRequest:request];

There are other built-in ways to handle content from responses. Be sure to read up the In-depth guide to response content handling.

Documentation

Likely TODO list

  • Multipart upload helpers
  • Follow redirects
  • Use curl's multi handles
  • Your bright idea here

For a comprehensive list, be sure to visit the Roadmap wiki page.

Why?

You mean other than its sleek API or the fact that it uses libcurl underneath?

Well, unlike NSURLConnection and, consequently, any lib that relies on it, BBHTTP...

  • is strictly compliant with section 8.2.3 of RFC 2616, a.k.a. the misbeloved Expect: 100-Continue header;
  • can receive server error responses midway through upload — as opposed to continuing to pump data into socket eden, and eventually reporting connection timeout instead of the actual error response sent by the server.

"But my uploads work just fine..."

  • If you only wrote code that uploads to a server, you've probably never noticed either of the above;
  • If you wrote both client and server-side code to handle uploads, chances are that you never ran into either of the above either;
  • If you're hardcore and wrote your own server and client and noticed NSURLConnection ignores errors until it finishes its upload, then this is the HTTP framework for you. Also, fistbump for writing your server and client. And paying attention to the specs.

On a more serious tone, the motivation for this libcurl wrapper was that during development of Droplr's API server, we noticed that whenever the API rejected an upload and immediately closed the connection — which is a perfectly legal & reasonable behavior — the Cocoa-based clients would keep reporting upload progress (even though I knew the socket was closed) and eventually fail with "Request timeout", instead of the response the server had sent down the pipes.

This meant that:

  1. NSURLConnection wasn't waiting for the 100-Continue provisional response before sending along the request body;
  2. NSURLConnection wasn't realizing that a response was already sent and the connection was dying until it finished uploading what it had to upload. stubborn bastard, eh?

I did file a bug report but after a year of waiting for a response, I decided to come up with a working alternative. Coincidentally, the same day I let this library out in the open, I got a reply from Apple — closing the bug as a duplicate of some other I don't have access to.

A couple of quick tests with command line version of curl proved that curl knew how to properly handle these edge cases so it was time to build a new HTTP framework for Cocoa.

During that process, this handy build script was produced, so even if you don't want to use this library but are still interested in getting curl running on iOS, do check it out!

Dependencies

  • libcurl (read below)
  • libz.dylib
  • Security.framework
  • CoreServices.framework on OSX, MobileCoreServices.framework on iOS
  • AppKit.framework on OSX, UIKit.framework on iOS

Note: You can find libcurl 7.30.0 binaries and headers under Build/iOS/Static lib/libcurl and Build/OSX/Static lib/libcurl. There are two versions for iOS, compiled against 6.1 SDK. libcurl.iOS.dev.a has support for for i386 (simulator), armv7 and armv7s (iPhone 3GS and newer) while libcurl.iOS.appstore.a only has support for arm architectures — making it smaller in size and thus optimized for releases. The OSX version was compiled against 10.8 SDK with support for x86_64 (64 bit Intel). If you'd like to build your own custom version, try this. All binaries are compiled with debug symbols so even though they appear large, they'll end up with 400~600KB.

Documentation

For guides on how to setup and start working with this lib, check out the wiki pages.

The project also includes comprehensive class-level documentation. If you happen to have appledoc installed, just run the generate script on the Docs folder and it'll create html documentation for you under Docs/html.

Acknowledgements

  • Daniel Stenberg and everyone else involved in making cURL and libcurl
  • Nick Zitzmann for the Secure Transport TLS/SSL curl plugin
  • Ben Copsey for ASIHTTPRequest, which has been my HTTP workhorse on iOS since day 0

License

BBHTTP is licensed under the Apache Software License version 2.0

Get in touch

I'm on twitter as @biasedbit. I also write every now and then.

bbhttp's People

Contributors

biasedbit avatar indragiek avatar jasperblues avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bbhttp's Issues

Method names

Instead of createResource/getResource, etc . . . how about using just get/post/put . .

I'm using the library for SOAPish type services and it certainly feels more natural in this case (I know exactly what I'm gonna get without reading docs). . . at the same time, in changing the method names I don't think it would hinder the experience when using the library for proper REST services. . . .what do you think?

FTP support?

I was looking at using BBHTTP for FTP support as well as HTTP. For FTP support is looked like the main method that would need to be overridden was the executeContext:withCurlHandle: of BBHTTPExecutor class? I would only need directory listing and download capability for now.

Publish API docs

Hi!

Nice lib! How about publishing the API docs on the github page? I use a script to get my build server to do it. Happy to share.

Can't execute request

Tried the following test case:

__block NSString* message;
[[BBHTTPRequest getResource:@"http:/www.google.com"] execute:^(BBHTTPResponse* r)
{
    NSLog(@"Finished: %u %@ -- received %u bytes of '%@'.", r.code, r.message, r.contentSize, r[@"Content-Type"]);
    message = r.message;
} error:^(NSError* e)
{
    NSLog(@"Request failed: %@", [e localizedDescription]);
}];

assertWillHappen(message != nil);

Expected it to pass, but got this response:

2013-03-18 14:10:02.684 otest[8577:707] *** Assertion failure in -[BBHTTPRequest setValue:forHeader:], /Users/jblues/Infraxis/mobile/iOS/Pods/BBHTTP/BBHTTP/BBHTTPRequest.m:269

value cannot be nil
Unknown.m:0: error: -[ClientTests test_some_http] : value cannot be nil

Clarification on 100-Continue issues

Hello,

I ran into a similar problem with NSURLConnection et al years ago, that 100-Continue support was non-existant.

Fairly recently I got round to filing a radar asking for this feature, and was surprised to receive a response that it is supported — since "at least" OS X 10.7 — simply by:

set the "Expect" header to "100-continue"

I'm curious, did you ever try this? Indeed, did you ever try dropping down to the CFHTTPStream level either?

Example on GH page doesn't work

This example:

[[BBHTTPRequest getResource:@"http://foo.bar/baz/1"] setup:^(id request) {
    // Prepare request...
} execute:^(BBHTTPResponse* response) {
    // Handle response...
} error:^(NSError* error) {
    // Handle error...
} finally:^{
    // Do after error OR success.
}];

... gives error, method does not exist... changing to readResource works.

Expose request timeout conditions as properties on BBHTTPRequest

I'm thinking about exposing both CURLOPT_LOW_SPEED_LIMIT and CURLOPT_LOW_SPEED_TIME as a single property:

struct BBTransferRate {
    unsigned long bytesPerSecond;
    NSTimeInterval duration;
};
typedef struct BBTransferRate BBTransferRate;

BBTransferRate BBTransferRateMake(unsigned long bytesPerSecond, NSTimeInterval duration)
{
    BBTransferRate threshold;
    threshold.bytesPerSecond = bytesPerSecond;
    threshold.duration = duration;

    return threshold;
}

NSString* NSStringFromBBTransferRate(BBTransferRate transferRate)
{
    return [NSString stringWithFormat:@"%lu/s for %.0f seconds", transferRate.bytesPerSecond, transferRate.duration];
}

I think it's way more meaningful than two separate properties.

The property on BBHTTPRequest should probably be named requestTimeoutThreshold.

Resumable uploads

Hello,
I could not find your project support large file upload with resume support, could you pls implement that..?

setup block parameter typed to id instead of BBHTTPRequest

[[BBHTTPRequest readResource:@"http://foo.bar/baz/1"] setup:^(id request) {
    // Prepare request...
} execute:^(BBHTTPResponse* response) {
    // Handle response...
} error:^(NSError* error) {
    // Handle error...
} finally:^{
    // Do after error OR success.
}];

... why is the setup block parameter typed to id? Is it a BBHTTPRequest?

Certificate pinning

I was reading this on Australian CocoaHeads and thought it might be a neat feature for BBHTTP:

Hi Gordon,

If you are using the AFNetworking library for communicating with your
web services, then you get the functionality to check for invalid
certificates out of the box, unless you #define
AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES. Furthermore,
AFNetworking also has built in support for "certificate pinning" (see
Loukas' email). You can enable this by #define
AFNETWORKING_PIN_SSL_CERTIFICATES 1.

Hope this helps as well.

Regards,
Sadat

Keep alive support?

Can you comment on support for keep-alive HTTP connections on OS X using BBHTTP? From my limited testing it appears NSURLConnection on OS X opens and closes a connection on every call. I know libcurl supports keep alive so I was wondering if this ability is accessible in BBHTTP?

Multiple response headers not supported

The current BBHTTPResponse contract doesn't allow for multiple response headers. The property headers references a dictionary of NSString,NSString, so you can't set the same header several times.

For example, Set-Cookie might be present several times in the same response.

I'm used to Java, and there is this method in HttpServletResponse (the Java equivalent of BBHTTPResponse) :
http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html#getHeaders(java.lang.String)

I have patched the lib in order to support that, and tried to keep backward compat.

I'll send a pull request ASAP.

Running Docs/Generate gives error

Running Docs/Generate gives error

Error is:

ERROR: AppledocException: Path or file '/Users/jblues/Loud-and-Clear/BBHTTP/Docs/../Classes' doesn't exist!

. . also tried running Project/build.xml script that I submitted a while back. . . but found out the script only wants to be run from the root directory. . needs to be changed to run from /Project dir.

Publish libCurl in github

If the libCurl is published in github (eg via build server) it world be easy to install this library via cocoapods

username and password

I am sorry for this question, but I can't find where to put user and password in a request.
I tried in URL, but it won't work for me.

Chain response handlers

Follow on from #16.

For me, composition always wins over inheritance. Chaining response handlers (responders?) would open up the doorway to some pretty cool stuff.

A good use case would be chaining a JSON parser to a responder which updates values on some model. The client wouldn't need to know about parsing JSON - it would essentially result in object mapping (which RestKit heavily relies on.)

Chaining diagram:

Request --> Selective Discarder --> JSON Parser --> Object Mapper

The client would then join the dots, fire a request, and that's it. SRP in action. Very similar to Apple's chain of responsibility, and to the Decorator design pattern.

Progress handling and error handling might also benefit from a similar pattern, e.g:

Request --> Selective Discarder --> Percentage Calculator --> Update Label (domain specific - deals with UI)

Or for errors:

Request --> Selective Discarder --> Error Localizer --> Alert Presenter (again, domain specific)

It would be necessary to have a concept of a compound response handler - one which forwards messages to multiple responders but appears as one object to the request - to handle responses differently:

Request --> Compound Handler --> Percentage Calculator --> Update Label
                            \--> Progress Bar Updater

I'm not sure how usable this would be to those uncomfortable with constructing a larger number of objects, or how difficult it would be implement. I have a feeling it's largely my preferred coding style leaking out, and that the best solution might be a compromise.

Advantages with BBHTTP over AFNetworking

Hi!

Could you in the readme say something about the advantages over say AFNetworking? I've read this part.

Well, unlike NSURLConnection and, consequently, any lib that relies on it, BBHTTP...

is strictly compliant with section 8.2.3 of RFC 2616, a.k.a. the misbeloved Expect: 100-Continue header;
can receive server error responses midway through upload — as opposed to continuing to pump data into socket eden, and eventually reporting connection timeout instead of the actual error response sent by the server.

And that's good. But I guess there is some other advantages and hidden gems maybe in the syntax and structure of BBHTTP over say AFNetworking?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.