curl - Sizuha/devdog GitHub Wiki

Build: Mac OS X, iOS

๋นŒ๋“œ ์Šคํฌ๋ฆฝํŠธ

ํ”„๋ ˆ์ž„์›Œํฌ ์ถ”๊ฐ€

Xcode์—์„œ ๋‹ค์Œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

  • libz.dylib
  • Security.framework

Easy Interface

libCURL์€ ์•„๋ž˜์™€ ๊ฐ™์€ easy๋ผ๋Š” ์ด๋ฆ„์„ ๊ฐ€์ง€๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

  1. curl_global_init : curl ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค.
  2. curl_easy_init : context๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  3. curl_easy_setopt : context ์„ค์ •
  4. curl_easy_perform : ์š”์ฒญ์„ ์ดˆ๊ธฐํ™” ํ•˜๊ณ  callbackํ•จ์ˆ˜๋ฅผ ๋Œ€๊ธฐ์‹œํ‚จ๋‹ค.
  5. curl_easy_cleanup : context๋ฅผ ์—†์•ค๋‹ค.
  6. curl_global_cleanup : curl ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์—†์•ค๋‹ค.

HTTP GET

curl_easy_init ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์„œ context ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

CURL *ctx = curl_easy_init();

curl_easy_setopt ๋ฅผ ์ด์šฉํ•ด์„œ context๊ฐ์ฒด๋ฅผ ์„ค์ •ํ•œ๋‹ค. CURLOPT_URL์€ ๋ชฉํ‘œ URL์ด๋‹ค.

curl_easy_setopt(ctx, CURLOPT_URL, argv[1]);

curl_easy_setopt๋ฅผ ์ด์šฉํ•˜๋ฉด ์ด์™ธ์—๋„ ๋ช‡ ๊ฐ€์ง€ ์„ค์ •์„ ๋” ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

curl_easy_setiopt(ctx, CURLOPT_WRITEHEADER, stderr);
curl_easy_setiopt(ctx, CURLOPT_WRITEDATA, stdout);

header ์ •๋ณด๋Š” ํ‘œ์ค€์—๋Ÿฌ๋กœ, body์ •๋ณด๋Š” ํ‘œ์ค€์ถœ๋ ฅ์œผ๋กœ ๊ฐ€์ ธ์˜ค๋„๋ก ์„ค์ •์„ ํ–ˆ๋‹ค.

์ด์ œ curl_easy_performํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด์„œ ์‹ค์ œ ํŽ˜์ด์ง€๋ฅผ ๊ธ์–ด์˜ค๋Š” ์ผ์„ ํ•˜๋„๋ก ํ•˜์ž.

const CURLcode rc = curl_easy_perform(ctx);
if (CURLE_OK != rc) {
   std::cerr << "Error from cURL: " << curl_easy_strerror(rc) std::endl;
}
else {
    // ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
}

์ดํ›„ ๋ฐ์ดํ„ฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด curl_easy_getinfo๋ฅผ ํ†ตํ•ด์„œ ์–ป์–ด์™€์„œ ์ฒ˜๋ฆฌํ•˜๋ฉด ๋œ๋‹ค.

long statLong;
curl_easy_getinfo(ctx, CURLINFO_HTTP_CODE, &statLong);
std::cout << "HTTP response code: " << statLong << std::endl;

์›ํ•˜๋Š” ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฐ์ดํ„ฐ์˜ ํƒ€์ž…์— ๋งž๋Š” ์ธ์ž๋ฅผ ์จ์•ผ ํ•œ๋‹ค. 200์ด๋‚˜ 404์™€ ๊ฐ™์€ HTTP ์‘๋‹ต์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธธ ์›ํ•œ๋‹ค๋ฉด CURLINFO_HTTP_CODE๋ฅผ ์ „์†ก๋ฐ›์€ ๋ฌธ์„œ์˜ ํฌ๊ธฐ๋ฅผ ์•Œ์•„๋‚ด๊ธธ ์›ํ•œ๋‹ค๋ฉด CURLINFO_SIZE_DOWNLOAD๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‹์ด๋‹ค.

๋ชจ๋“  ์ž‘์—…์ด ๋‹ค ๋๋‚ฌ๋‹ค๋ฉด, curl_easy_cleanup์„ ํ˜ธ์ถœํ•ด์„œ curl_easy_setopt ๊ฐ์ฒด๋ฅผ ์†Œ๋ฉธ์‹œ์ผœ์•ผ ํ•œ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์„๊ฒฝ์šฐ ๋ฉ”๋ชจ๋ฆฌ๋ˆ„์ˆ˜ ํ˜„์ƒ์„ ๊ฒช๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

/*
 sample for O'ReillyNet article on libcURL:
    {TITLE}
    {URL}
    AUTHOR: Ethan McCallum
 
 Scenario: use http/GET to fetch a webpage
 
 ์ด ์ฝ”๋“œ๋Š” Ubuntu ๋ฆฌ๋ˆ…์Šค Kernel 2.6.15์—์„œ 
 libcURL ๋ฒ„์ ผ 7.15.1๋กœ ํ…Œ์ŠคํŠธ ๋˜์—ˆ๋‹ค.
 2006๋…„ 8์›” 3์ผ
 */
 
#include <iostream>
 
extern "C" {
    #include <curl/curl.h>
}
 
// - - - - - - - - - - - - - - - - - - - -
 
enum {
    ERROR_ARGS = 1 ,
    ERROR_CURL_INIT = 2
} ;
 
enum {
    OPTION_FALSE = 0 ,
    OPTION_TRUE = 1
} ;
 
enum {
    FLAG_DEFAULT = 0 
} ;
 
// - - - - - - - - - - - - - - - - - - - -
  
int main( const int argc , const char** argv ) {
    if( argc != 2 ){
        std::cerr << " Usage: ./" << argv[0] << " {url} [debug]" << std::endl ;
        return( ERROR_ARGS ) ;
    }
 
    const char* url = argv[1] ;
 
    // lubcURL ์ดˆ๊ธฐํ™” 
    curl_global_init( CURL_GLOBAL_ALL ) ;
 
    // context๊ฐ์ฒด์˜ ์ƒ์„ฑ
    CURL* ctx = curl_easy_init() ;
 
    if( NULL == ctx ){
        std::cerr << "Unable to initialize cURL interface" << std::endl ;
        return( ERROR_CURL_INIT ) ;
    }
 
    // context ๊ฐ์ฒด๋ฅผ ์„ค์ •ํ•œ๋‹ค.    
    // ๊ธ์–ด์˜ฌ url์„ ๋ช…์‹œํ•˜๊ณ , url์ด URL์ •๋ณด์ž„์„ ์•Œ๋ ค์ค€๋‹ค.
    curl_easy_setopt( ctx , CURLOPT_URL,  url ) ;
 
    // no progress bar:
    curl_easy_setopt( ctx , CURLOPT_NOPROGRESS , OPTION_TRUE ) ;
 
    /*
    By default, headers are stripped from the output.
    They can be:
 
    - passed through a separate FILE* (CURLOPT_WRITEHEADER)
 
    - included in the body's output (CURLOPT_HEADER -> nonzero value)
        (here, the headers will be passed to whatever function
         processes the body, along w/ the body)
 
    - handled with separate callbacks (CURLOPT_HEADERFUNCTION)
        (in this case, set CURLOPT_WRITEHEADER to a
         matching struct for the function)
 
    */
    
    // ํ—ค๋”๋Š” ํ‘œ์ค€์—๋Ÿฌ๋กœ ์ถœ๋ ฅํ•˜๋„๋ก ํ•˜๋‹ค. 
    curl_easy_setopt( ctx , CURLOPT_WRITEHEADER , stderr ) ;
 
 
    // body ๋ฐ์ดํ„ฐ๋Š” ํ‘œ์ค€์ถœ๋ ฅ ํ•˜๋„๋ก ํ•œ๋‹ค.
    curl_easy_setopt( ctx , CURLOPT_WRITEDATA , stdout ) ;
 
    // context ๊ฐ์ฒด์˜ ์„ค์ • ์ข…๋ฃŒ 
 
 
    // ์›นํŽ˜์ด์ง€๋ฅผ ๊ธ์–ด์˜จ๋‹ค. 
 
    const CURLcode rc = curl_easy_perform( ctx ) ;
 
    if( CURLE_OK != rc ){
 
        std::cerr << "Error from cURL: " << curl_easy_strerror( rc ) << std::endl ;
 
    } else {
 
        // get some info about the xfer:
        double statDouble ;
        long statLong ;
        char* statString = NULL ;
 
        // HTTP ์‘๋‹ต์ฝ”๋“œ๋ฅผ ์–ป์–ด์˜จ๋‹ค. 
        if( CURLE_OK == curl_easy_getinfo( ctx , CURLINFO_HTTP_CODE , &statLong ) ){
            std::cout << "Response code:  " << statLong << std::endl ;
        }
 
        // Content-Type ๋ฅผ ์–ป์–ด์˜จ๋‹ค.
        if( CURLE_OK == curl_easy_getinfo( ctx , CURLINFO_CONTENT_TYPE , &statString ) ){
            std::cout << "Content type:   " << statString << std::endl ;
        }
 
        // ๋‹ค์šด๋กœ๋“œํ•œ ๋ฌธ์„œ์˜ ํฌ๊ธฐ๋ฅผ ์–ป์–ด์˜จ๋‹ค.
        if( CURLE_OK == curl_easy_getinfo( ctx , CURLINFO_SIZE_DOWNLOAD , &statDouble ) ){
            std::cout << "Download size:  " << statDouble << "bytes" << std::endl ;
        }
 
        // 
        if( CURLE_OK == curl_easy_getinfo( ctx , CURLINFO_SPEED_DOWNLOAD , &statDouble ) ){
            std::cout << "Download speed: " << statDouble << "bytes/sec" << std::endl ;
        }
 
    }
 
    // cleanup
    curl_easy_cleanup( ctx ) ;
    curl_global_cleanup() ;
 
    return( 0 ) ;
}

HTTP POST

POST๋กœ ๋ณด๋‚ด๋Š” ๋ฐ์ดํ„ฐ๋Š” key=valueํ˜•ํƒœ๋กœ ๋˜์–ด ์žˆ์œผ๋ฉฐ, ๊ฐ๊ฐ์˜ key=value ๋Š” &๋ฅผ ํ†ตํ•ด์„œ ๊ตฌ๋ถ„๋œ๋‹ค.

const char* postData="param1=value1ยถm2=value2&...";

POST ๋ฐ์ดํ„ฐ ์ „์†ก์„ ์œ„ํ•ด์„œ CURLOPT_POSTFIELDS์˜ต์…˜์„ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.

curl_easy_setopt(ctx, CURLOPT_POSTFIELDS, postData);

์ด์ œ CURLOPT_HTTPHEADER๋ฅผ ์ด์šฉํ•ด์„œ ์‚ฌ์šฉ์ž์ •์˜ HTTP ํ—ค๋”๋ฅผ ๋งŒ๋“ค๋„๋ก ํ•œ๋‹ค.

curl_sist * responseHeaders=NULL;
 
responseHeaders = curl_slist_append(
    responseHeaders,
    "Expect: 100-continue"
);

curl_easy_setopt(ctx, CURLOPT_HTTPHEADER, responseHeaders);

์ฃผ์˜ ํ• ๊ฒƒ์€ libCURL์€ hidden ํ•„๋“œ๋‚˜ JavaScript์™€ ๊ฐ™์€ ํด๋ผ์ด์–ธํŠธ์ธก์˜ ๊ธฐ์ˆ ๋“ค์„ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด ํผ์ž…๋ ฅ์„ ํ•˜๊ณ ๋‚˜์„œ submit๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํผ์˜ ๊ฐ ํ•„๋“œ๋ฅผ ๊ฒ€์‚ฌํ•˜๋Š” ๋“ฑ์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋“ฑ์€ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๋‹ค.

/*
 sample for O'ReillyNet article on libcURL:
    {TITLE}
    {URL}
    AUTHOR: Ethan McCallum
 
 HTTP POST (e.g. form processing or REST web services)
 
 ์ด ์ฝ”๋“œ๋Š” Ubuntu 6.06 Dapper Drake,  
 libcURL
 This code was built/tested under Fedora Core 3,
 libcURL version 7.12.3 ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธ ๋˜์—ˆ๋‹ค.
 */
 
#include <cstdio>
#include <iostream>
#include <string>
#include <sstream>
 
extern "C" {
    #include <curl/curl.h>
}
 
// - - - - - - - - - - - - - - - - - - - -
 
enum {
    ERROR_ARGS = 1 ,
    ERROR_CURL_INIT = 2
} ;
 
enum {
    OPTION_FALSE = 0 ,
    OPTION_TRUE = 1
} ;
 
enum {
    FLAG_DEFAULT = 0 
} ;
 
const char* targetUrl ;
 
// - - - - - - - - - - - - - - - - - - - -
 
int main( int argc , char** argv ) { 
    if( argc != 2 ){
        std::cerr << "test of libcURL: test an HTTP post" << std::endl ;
        std::cerr << "(post data is canned)" << std::endl ;
        std::cerr << " Usage: " << argv[0] << " {post url}" << std::endl ;
        std::exit( ERROR_ARGS ) ;
    }
 
    targetUrl = argv[1] ;
 
    curl_global_init( CURL_GLOBAL_ALL ) ;
 
 
    CURL* ctx = curl_easy_init() ;
 
    if( NULL == ctx ){
        std::cerr << "Unable to initialize cURL interface" << std::endl ;
        return( ERROR_CURL_INIT ) ;
    }
 
    /* BEGIN: configure the handle: */
 
    // Target URL: 
    curl_easy_setopt( ctx , CURLOPT_URL,  targetUrl ) ;
    // no progress bar:
    curl_easy_setopt( ctx , CURLOPT_NOPROGRESS , OPTION_TRUE ) ;
 
    // ์‘๋‹ต๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์ค€์ถœ๋ ฅ์œผ๋กœ ๋ณด๋‚ธ๋‹ค. 
    curl_easy_setopt( ctx , CURLOPT_WRITEDATA , stdout ) ;
 
    // ์‚ฌ์šฉ์ž ์ •์˜ HTTP ํ—ค๋”: create a linked list and assign
    curl_slist* responseHeaders = NULL ;
    responseHeaders = curl_slist_append( responseHeaders , "Expect: 100-continue" ) ;
    responseHeaders = curl_slist_append( responseHeaders , "User-Agent: Some Custom App" ) ;
    curl_easy_setopt( ctx , CURLOPT_HTTPHEADER , responseHeaders ) ;
 
    // POST Data ์„ค์ • 
    // notice the URL-unfriendly characters (such as "%" and "&")
    // URL์—์„œ๋Š” '%', '&', ' '์™€ ๊ฐ™์€ ๋ฌธ์ž๋ฅผ URL encoding ์‹œ์ผœ์ค˜์•ผ ํ•œ๋‹ค.
    // curl_escape ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด์„œ ์ธ์ฝ”๋”ฉํ•  ์ˆ˜ ์žˆ๋‹ค. 
    const char* postParams[] = {
        "One"      , "this has % and & symbols" ,
        "Dos"      , "value with spaces" ,
        "Trois"    , "plus+signs+will+be+escaped" ,
        "Chetirye" , "fourth param..." ,
        NULL
    } ; 
 
    // buffer for the POST params
    std::ostringstream postBuf ;
 
    const char** postParamsPtr = postParams ;
 
    while( NULL != *postParamsPtr )
    {
        // curl_escape( {string} , 0 ): replace special characters
        // (such as space, "&", "+", "%") with HTML entities.
        // ( 0 => "use strlen to find string length" )
        // remember to call curl_free() on the strings on the way out
        char* key = curl_escape( postParamsPtr[0] , FLAG_DEFAULT ) ;
        char* val = curl_escape( postParamsPtr[1] , FLAG_DEFAULT )  ;
 
        std::cout << "Setting POST param: "" << key << "" => "" << val << """ << std::endl ;
        postBuf << key << "=" << val << "&" ;
 
        postParamsPtr += 2 ;
 
        // the cURL lib allocated the escaped versions of the
        // param strings; we must free them here
        curl_free( key ) ;
        curl_free( val ) ;
 
    }
    postBuf << std::flush ;
 
    // We can't really call "postBuf.str().c_str()" here, because
    // the std::string created in the middle is a temporary.  In turn,
    // the char* buf from its c_str() operation isn't guaranteed to
    // be around after the function call.
    // The solution: explicitly create the string.
 
    // Larger (and/or better) code would use std::string::copy() to create
    // a const char* pointer to pass to cURL, then clean it up later.
    // e.g.:
    //    const char* postData = new char*[ 1 + postBuf.tellg() ] ;
    //     postBuf.str().copy( postData , std::string::npos ) ;
    //  postData[ postBuf.tellg() ] == '' ;
    const std::string postData = postBuf.str() ;
 
    std::cout << "post data: " << postData << std::endl ;
    curl_easy_setopt( ctx , CURLOPT_POSTFIELDS , postData.c_str() ) ;
 
    // do a standard HTTP POST op
    // in theory, this is automatically set for us by setting
    // CURLOPT_POSTFIELDS...
    curl_easy_setopt( ctx , CURLOPT_POST , OPTION_TRUE ) ;
 
    /* END: configure the handle */
    // action!
    std::cout << "- - - BEGIN: response - - -" << std::endl ;
    CURLcode rc = curl_easy_perform( ctx ) ;
    std::cout << "- - - END: response - - -" << std::endl ;
 
    // "curl_easy_strerror()" available in curl v7.12.x and later
    if( CURLE_OK != rc ){
        std::cerr << 't' << "Error from cURL: " << curl_easy_strerror( rc ) << std::endl ;
    }
 
    // cleanup
    curl_slist_free_all( responseHeaders ) ;
    curl_easy_cleanup( ctx ) ;
    curl_global_cleanup() ;
    std::exit( 0 ) ;
 }

Time out

// ํƒ€์ž„์•„์›ƒ ์„ค์ •: ๋‹จ์œ„๋Š” ์ดˆ(sec)
void setTimeout(CURL* curl, int conn_timeout, int http_timeout)
{
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, conn_timeout); // ์—ฐ๊ฒฐ์‹œ ํƒ€์ž„์•„์›ƒ
  curl_easy_setopt(curl, CURLOPT_TIMEOUT, http_timeout); // ๋ฐ์ดํ„ฐ ์ „์†ก ํƒ€์ž„์•„์›ƒ
}

milliseconds ๋‹จ์œ„๋กœ ์„ค์ •ํ•˜๋ ค๋ฉด CURLOPT_TIMEOUT_MS ์˜ต์…˜์œผ๋กœ ์„ค์ •.

โš ๏ธ **GitHub.com Fallback** โš ๏ธ