{"id":14990005,"url":"https://github.com/zymxxxs/ymhttp","last_synced_at":"2025-08-21T01:32:24.003Z","repository":{"id":56929537,"uuid":"235064278","full_name":"zymxxxs/YMHTTP","owner":"zymxxxs","description":"基于 libcurl 的 IO 多路复用 HTTP 框架，适用于 iOS 平台，支持 HTTP/HTTPS/HTTP2/DNS(SNI)","archived":false,"fork":false,"pushed_at":"2020-06-24T15:19:08.000Z","size":17579,"stargazers_count":185,"open_issues_count":13,"forks_count":33,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-08-01T13:33:48.535Z","etag":null,"topics":["dns","http","http2","https","ios","libcurl","libressl","nsurlsession","sni","tls"],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zymxxxs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-01-20T09:29:13.000Z","updated_at":"2025-07-16T08:57:04.000Z","dependencies_parsed_at":"2022-08-21T00:01:10.455Z","dependency_job_id":null,"html_url":"https://github.com/zymxxxs/YMHTTP","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/zymxxxs/YMHTTP","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zymxxxs%2FYMHTTP","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zymxxxs%2FYMHTTP/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zymxxxs%2FYMHTTP/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zymxxxs%2FYMHTTP/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zymxxxs","download_url":"https://codeload.github.com/zymxxxs/YMHTTP/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zymxxxs%2FYMHTTP/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271415043,"owners_count":24755629,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-20T02:00:09.606Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dns","http","http2","https","ios","libcurl","libressl","nsurlsession","sni","tls"],"created_at":"2024-09-24T14:19:19.209Z","updated_at":"2025-08-21T01:32:23.130Z","avatar_url":"https://github.com/zymxxxs.png","language":"Objective-C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# YMHTTP\n\n[![Build Status](https://travis-ci.org/zymxxxs/YMHTTP.svg?branch=master)](https://travis-ci.org/zymxxxs/YMHTTP)\n[![Version](https://img.shields.io/cocoapods/v/YMHTTP.svg?style=flat)](https://cocoapods.org/pods/YMHTTP)\n[![License](https://img.shields.io/cocoapods/l/YMHTTP.svg?style=flat)](https://cocoapods.org/pods/YMHTTP)\n[![Platform](https://img.shields.io/cocoapods/p/YMHTTP.svg?style=flat)](https://cocoapods.org/pods/YMHTTP)\n\n\n`YMHTTP` 是一个适用于 iOS 平台，基于  [libcurl](https://curl.haxx.se/) 的 IO 多路复用 HTTP 框架，其 API 设计和行为与 NSURLSession 保持高度一致。\n\n因为 `YMHTTP` 是基于 libcurl 进行封装，所以有着较高的定制性，目前的版本与 `NSURLSession` 在 API 保持高度一致的同时拓展了 DNS 的能力（包括 SNI 的场景）。\n\n如果你对 DNS 相关的问题比较感兴趣，可以查看这个 [文章](https://juejin.im/post/5e6a4cfc518825495b29ad65)，它汇总了在 iOS 上支持 DNS 需要面临的一些技术难点、相关解决方案以及 YMHTTP 诞生的初衷。\n\n## 说明\n\n1. 您可以通过 [NSURLSession](https://developer.apple.com/documentation/foundation/nsurlsession) 来查阅具体的细节。\n2. 这里有一份非常不错的[NSURLSession最全攻略](https://mp.weixin.qq.com/s/UdwRytczg2lar0FwRpLTyg)可以查漏补缺(来自搜狐技术产品)。\n3. YMHTTP 和 NSURLSession 非常像，一个是 YM 前缀，一个是 NS 前缀，对外提供的API相互一致\n4. 如果您已经非常了解 NSURLSession，那么可以直接查阅 Connect to specific host and port 部分来获取 DNS 相关内容\n5. 不支持 System Background Task 相关功能，这个真的无能为力\n\n## 安装\n\n```ruby\npod 'YMHTTP', '~\u003e 1.0'\n```\n\n## Requirements\n\n* iOS 10.0 \n* Xcode 11.3.1\n* libcurl 7.64.1 + SecureTransport\n\n## 使用\n\n### 0x01 YMSession\n\n```objc\n// 创建 sharedSession\nYMURLSession *sharedSession = [YMURLSession sharedSession];\n\n// 使用指定配置创建会话\nYMURLSessionConfiguration *config = [YMURLSessionConfiguration defaultSessionConfiguration];\nYMURLSession *sessionNoDelegate = [YMURLSession sessionWithConfiguration:config];\n\n// 创建具有指定会话配置，委托和操作队列的会话\nYMURLSession *session = [YMURLSession sessionWithConfiguration:config\n                                                      delegate:self\n                                                 delegateQueue:nil];\n```\n\n### 0x02 Adding Data Task to a Session\n\n```objc\n- (YMURLSessionTask *)taskWithRequest:(NSURLRequest *)request;\n- (YMURLSessionTask *)taskWithURL:(NSURL *)url;\n```\n\n通过指定 URL 或 Request 来创建一个任务\n\n```objc\n- (YMURLSessionTask *)taskWithRequest:(NSURLRequest *)request\n                    completionHandler:(void (^)(NSData *_Nullable data,\n                                                NSHTTPURLResponse *_Nullable response,\n                                                NSError *_Nullable error))completionHandler;\n\n- (YMURLSessionTask *)taskWithURL:(NSURL *)url\n                completionHandler:(void (^)(NSData *_Nullable data,\n                                            NSHTTPURLResponse *_Nullable response,\n                                            NSError *_Nullable error))completionHandler;\n```\n\n通过指定 URL 或 Request 来创建任务，在任务完成后调用 completionHandler\n\n#### Example\n\n1. delegate 方式\n\n```objc\n// create\nYMURLSessionTask *task = [session taskWithURL:[NSURL URLWithString:@\"http://httpbin.org/get\"]];\n[task resume];\n\n// delegate\n- (void)YMURLSession:(YMURLSession *)session task:(YMURLSessionTask *)task didCompleteWithError:(NSError *)error {\n\n}\n\n- (void)YMURLSession:(YMURLSession *)session task:(YMURLSessionTask *)task didReceiveData:(NSData *)data {\n\n}\n\n- (void)YMURLSession:(YMURLSession *)session task:(YMURLSessionTask *)task willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse * _Nullable))completionHandler {\n    completionHandler(proposedResponse);\n}\n\n-(void)YMURLSession:(YMURLSession *)session task:(YMURLSessionTask *)task didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(YMURLSessionResponseDisposition))completionHandler {\n    completionHandler(YMURLSessionResponseAllow);\n}\n```\n\n2. completionHandler 方式\n\n```objc\nYMURLSessionTask *task = [session taskWithURL:[NSURL URLWithString:@\"http://httpbin.org/get\"] completionHandler:^(NSData * _Nullable data, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error) {\n    \n}];\n[task resume];\n```\n\n### 0x03 Adding Upload Tasks to a Session\n\n```objc\n- (YMURLSessionTask *)taskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;\n\n- (YMURLSessionTask *)taskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;\n\n- (YMURLSessionTask *)taskWithStreamedRequest:(NSURLRequest *)request;\n\n```\n\n通过指定 Request 来创建一个上传任务。\n\n`taskWithStreamedRequest` 方法，会调用 `YMURLSession:task:needNewBodyStream:` delegate 方法，您需要通过 `completionHandler` 返回一个 `NSInputStream` 对象。当然你也可以功过 `NSURLMutableRequest` 创建对象，并在 `bodyStream` 传入 `NSInputStream` 对象。\n\n如果您需要上传大文件，建议您使用 `fromFile` 方法，虽然 `taskWithStreamedRequest` 也支持大文件的传输，但其形式为循环执行 `读取指定长度内容 -\u003e 上传该内容`，该行为在内部的线程是同步的，而 `fromFile` 方式则会每次异步获取 3 * CURL_MAX_WRITE_SIZE 长度的内容供 libcurl 进行上传（CURL_MAX_WRITE_SIZE 为单次支持最大上传的长度），不仅减少文件 IO 的次数，也减少同步阻塞的时间，优化上传效率。\n\n```objc\n- (YMURLSessionTask *)taskWithRequest:(NSURLRequest *)request\n                             fromFile:(NSURL *)fileURL\n                    completionHandler:(void (^)(NSData *_Nullable data,\n                                                NSHTTPURLResponse *_Nullable response,\n                                                NSError *_Nullable error))completionHandler;\n\n- (YMURLSessionTask *)taskWithRequest:(NSURLRequest *)request\n                             fromData:(nullable NSData *)bodyData\n                    completionHandler:(void (^)(NSData *_Nullable data,\n                                                NSHTTPURLResponse *_Nullable response,\n                                                NSError *_Nullable error))completionHandler;\n```\n\n通过指定 Request 来创建任务，在任务完成后调用 completionHandler\n\n\n### 0x04 Adding Download Tasks to a Session\n\n```objc\n- (YMURLSessionTask *)taskWithDownloadRequest:(NSURLRequest *)request;\n\n- (YMURLSessionTask *)taskWithDownloadURL:(NSURL *)url;\n```\n\n通过指定 Request 来创建一个下载任务，并返回临时文件，由于该文件是临时文件，因此必须打开该文件进行读取，或将其移动到应用程序沙盒容器目录中的永久位置，支持大文件下载。\n\n当然你也可以使用 `taskWithRequest` 和 `taskWithURL` 来自定义下载任务。\n\n```objc\n- (YMURLSessionTask *)taskWithDownloadRequest:(NSURLRequest *)request\n                            completionHandler:(void (^)(NSURL *_Nullable location,\n                                                        NSHTTPURLResponse *_Nullable response,\n                                                        NSError *_Nullable error))completionHandler;\n\n- (YMURLSessionTask *)taskWithDownloadURL:(NSURL *)url\n                        completionHandler:(void (^)(NSURL *_Nullable location,\n                                                    NSHTTPURLResponse *_Nullable response,\n                                                    NSError *_Nullable error))completionHandler;\n```\n\n### 0x05 Connect to specific host and port\n\n```objc\n- (YMURLSessionTask *)taskWithURL:(NSURL *)url connectToHost:(NSString *)host;\n\n- (YMURLSessionTask *)taskWithURL:(NSURL *)url connectToHost:(NSString *)host connectToPort:(NSInteger)port;\n\n// 创建包含 host port 的 request\n[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@\"https://httpbin.org/get\"] connectToHost:@\"52.202.2.199\"];\n[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@\"https://httpbin.org/get\"] connectToHost:@\"52.202.2.199\" connectToPort:443];\n[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@\"https://httpbin.org/get\"]\n                           connectToHost:@\"52.202.2.199\"\n                           connectToPort:443\n                             cachePolicy:NSURLRequestUseProtocolCachePolicy\n                         timeoutInterval:60];\n```\n\n连接到特定的主机和端口，其中 host 支持 IP 的形式。如果使用正常的域名+host+port的请求方式，那么对于框架内部可以自动处理 Cookie，Cache 以及 302 等问题，当然该接口也支持 SNI 的场景。\n\n备注：该接口不会影响到 DNS Cache，了解更多可以看这里 https://curl.haxx.se/libcurl/c/CURLOPT_CONNECT_TO.html\n\n## libcurl\n\n\u003e libcurl is free, thread-safe, IPv6 compatible, feature rich, well supported, fast, thoroughly documented and is already used by many known, big and successful companies.\n\n\u003e libcurl是免费的，线程安全的，IPv6兼容的，功能丰富，支持良好，速度快，有完整的文档记录，已经被许多知名的，大的和成功的公司使用。\n\n您也可以通过这里查看更多的内容：https://curl.haxx.se/\n\n### libcurl 版本\n当前使用 libcurl 7.64.1 的版本，与 macOS Catalina 中保持一致，使用[curl-android-ios](https://github.com/gcesarmza/curl-android-ios)进行构建，你也可以选择喜欢的版本进行构建\n\n### HTTP/2\n目前版本不支持 HTTP/2，你可以使用 [Build-OpenSSL-cURL](https://github.com/jasonacox/Build-OpenSSL-cURL.git) 进行构建支持 HTTP/2 功能的版本。\n\n注意 `Build-OpenSSL-cURL` 中使用的是 openSSL，而目前 macOS Catalina 中则是使用 LibreSSL，我在 `Build-OpenSSL-cURL` 的基础上修改了一个支持 `libresll` 的版本，链接地址：https://github.com/zymxxxs/libcurl-nghttp2-libressl-ios\n\n备注：\n* 支持 HTTP/2 需要考虑包大小的影响\n* 目前一期的工作量主要是对外接口与 NSURLSession 对齐以及支持 DNS，HTTP/2 暂时不在支持范围内，所以上述脚本只能保证构建出静态库，暂无做过较多的验证，请知悉。\n\n## 最后\n\n如果看过 `swift-corelibs-foundation` 的相关源码， 你会发现其 `NSURLSession` 系列的功能（HTTP、FTP）则是基于 libcurl 进行的封装。如果你想学习它，建议你直接去学习官方源码，YMHTTP 也是参考其中大量的实现，然后补充一些尚未实现的功能以及修复了一些BUG。\n\n`YMHTTP` 之所以诞生，初衷是为了 `彻底` 解决 HTTP DNS 的问题（性能+SNI 场景+ Cache + Cookies + 302 ），现在回头来看，倒像是切开了一道口，获取了更多的自由度，如果您需要什么功能，请 ISSUE 告诉我。\n\n## 如何贡献\n\n非常欢迎你的加入! [提一个Issue](https://github.com/zymxxxs/YMHTTP/issues/new) 或者提交一个 Pull Request。\n\n## License\n\n**YMHTTP**  is available under the MIT license. See the LICENSE file for more info.\n\n## 感谢\n\n* [lindean](https://github.com/lindean) 破老师，目前就职于PDD。感谢其初版 HTTP DNS 的实现，作为先驱者，填了无数坑，尤其是 libcurl 中各种参数以及 Cache 层的相关实现\n* [amendgit](https://github.com/amendgit) 二老师，人称二哥，目前就职于支付宝，感谢其在 `IO 多路复用` 上解惑与指导\n* [libcurl](https://curl.haxx.se/libcurl/)\n* [swift-corelibs-foundation](https://github.com/apple/swift-corelibs-foundation.git)\n* [curl-android-ios](https://github.com/gcesarmza/curl-android-ios)\n* [Build-OpenSSL-cURL](https://github.com/jasonacox/Build-OpenSSL-cURL.git)\n\n## TODO:\n\n* 目前指定 IP 的能力通过 CURLOPT_CONNECT_TO 来解决，其好处是不会影响 DNS Cache，但是在 Charles 中会直接显示 IP 的请求。待考虑是否替换为 CURLOPT_RESOLVE 参数，不过对于 DNS Cache 的问题，是需要影响还是不能影响需要删除？或者是说DNS的能力，使用 CURLOPT_CONNECT_TO 还是 CURLOPT_RESOLVE 哪一个更为合理？\n* 目前大部分还是基于 AFNetworking 进行分封装，待考虑是否提供一个 YMNetworking 版本便于接入？也可以参考 retrofit 的接口实现？\n* NSURLSessionTaskMetrics 待实现。使用 curl_easy_getinfo 实现，目前实现这个功能，代码改动较大，需要着重设计下，目前属于重要不紧急，可以延后。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzymxxxs%2Fymhttp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzymxxxs%2Fymhttp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzymxxxs%2Fymhttp/lists"}