One of my friend asked me if there was a tool to send pipelined http request from linux command line. Thanks to cURL libcurl, I developed a small tool to do the same. Have put in some basic validations and some restrictions like the maximum no of pipeline request is 32 etc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | /* * Ahamed * Using cURL libcurl * * For sending pipelined http requests * Usage : ./send_pipelined_http <no of pipelined requests> <http://x.x.x.x> * Some basic validations added. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <curl/curl.h> static void usage(); #define ERR_LEN 512 #define MIN_URL_LEN 14 #define MAX_URL_LEN 32 #define MIN_PIPELINE_REQ 1 #define MAX_PIPELINE_REQ 32 #define err(x, y) {printf("\nError: %s...\nExiting...\n", x); if(y)exit(0); usage();} static unsigned int pipeline_req; static char url[MAX_URL_LEN]; static char err_str[ERR_LEN]; static void handle_curl_error(CURLcode ret, char *func) { if(ret != CURLE_OK){ fprintf(stderr, "%s() failed: %s\n", func, curl_multi_strerror(ret)); } return; } static void usage() { printf("\nDeveloped by Ahamed using cURL API via libcurl\n"); printf("Usage : ./send_pipelined_http <no of req> <http://x.x.x.x>\n"); printf(" Pipeline Request - Min %d : Max : %d\n", MIN_PIPELINE_REQ, MAX_PIPELINE_REQ); printf(" Hostname not supported yet. Please provide IP address.\n"); printf(" URL - Min Length %d : Max Length : %d\n\n", MIN_URL_LEN, MAX_URL_LEN); exit(0); } static void parse_args(int argc, char **argv) { if(argc <= 2) err("Insufficient arguments", 0); if(!argv) err("Something unexpected happened", 1); pipeline_req = atol(argv[1]); if(pipeline_req > MAX_PIPELINE_REQ || pipeline_req < MIN_PIPELINE_REQ) err("Invalid Pipeline Request", 0); if(strlen(argv[2]) > MAX_URL_LEN || strlen(argv[2]) < MIN_URL_LEN) err("Invalid URL length", 0); strncpy(url, argv[2], MAX_URL_LEN); return; } int main(int argc, char **argv) { int i=0; parse_args(argc, argv); CURLM *m_curl; CURLMcode res; m_curl = curl_multi_init(); curl_multi_setopt(m_curl, CURLMOPT_PIPELINING, 1L); CURL *curl[MAX_PIPELINE_REQ]; for(i=0; i<pipeline_req; i++){ curl[i] = curl_easy_init(); if(!curl[i]) err("Something went wrong", 0); res = curl_easy_setopt(curl[i], CURLOPT_URL, url); handle_curl_error(res, "curl_easy_setopt"); res = curl_easy_setopt(curl[i], CURLOPT_FOLLOWLOCATION, 1L); handle_curl_error(res, "curl_easy_setopt"); res = curl_multi_add_handle(m_curl, curl[i]); handle_curl_error(res, "curl_multi_add_handle"); } int ret = 1; do { res = curl_multi_perform(m_curl, &ret); } while(ret); handle_curl_error(res, (char*)__FUNCTION__); for(i=0; i<pipeline_req; i++){ curl_multi_remove_handle(m_curl, curl[i]); curl_easy_cleanup(curl[i]); } curl_multi_cleanup(m_curl); return 0; } |
root@Imperfecto_maximuS> gcc send_pipelined_http.c -lcurl -o send_pipelined_http root@Imperfecto_maximuS> ./send_pipelined_http Error: Insufficient arguments... Exiting... Developed by Ahamed(ahamed.en@gmail.com) using cURL API via libcurl Usage : ./send_pipelined_http <no of req> <http://x.x.x.x> Pipeline Request - Min 1 : Max : 32 Hostname not supported yet. Please provide IP address. URL - Min Length 14 : Max Length : 32 root@Imperfecto_maximuS> ./send_pipelined_http 3 http://11.1.1.99 <html><body><h1>This is sample page</h1>\0<p>for testing created...</p></body></html> <html><body><h1>This is sample page</h1>\0<p>for testing created...</p></body></html> <html><body><h1>This is sample page</h1>\0<p>for testing created...</p></body></html>
Hi, Thanks for the program, I am getting the below error when executing. I am using gcc version 4.1.2 20080704 (Red Hat 4.1.2-46)
ReplyDeleteand CURL version curl 7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5
[build@iforge1 venkat]$ gcc pipeline.c -lcurl -o pipeline
pipeline.c: In function âmainâ:
pipeline.c:80: error: âCURLMOPT_PIPELININGâ undeclared (first use in this function)
pipeline.c:80: error: (Each undeclared identifier is reported only once
pipeline.c:80: error: for each function it appears in.)
Venkat,
DeleteSorry I just got to see the comment. Request you to install the latest curl version available and try.
Hi Ahmed, Thanks for the response. I complied the code successfully, but I get this issue during runtime. Looking to solve this issue,
Delete[root@hello_38 venkat]# ./pipeline 3 http://www.venkat.com
./pipeline: error while loading shared libraries: libcurl.so.4: cannot open shared object file: No such file or directory
Running "ldconfig" solved the issue. Cheers !! But I suppose these are not piplined connection !! A pipelined connection uses same TCP socket from client. When I did a pcap and checked they are using different TCP sockets to send the requests and not the same. It actually send concurrent connections with three different client TCP sockets !!
DeleteWell I will be damned! Let me double check that.
DeleteSince I am harnessing the cURL library I believe it should work!
From pcap how did you know it was using 3 different sockets?
I checked it again, all the requests are send via only one TCP connection and they are send before the response for the first arrives.
DeleteIts not working can you pls help me getting below error
ReplyDeletesend_pipelined_http.c:10:19: stdio.h: No such file or directory
send_pipelined_http.c:11:20: stdlib.h: No such file or directory
send_pipelined_http.c:12:20: string.h: No such file or directory
send_pipelined_http.c:13:23: curl/curl.h: No such file or directory
send_pipelined_http.c:28: error: parse error before "ret"
send_pipelined_http.c: In function `handle_curl_error':
send_pipelined_http.c:30: error: `ret' undeclared (first use in this function)
send_pipelined_http.c:30: error: (Each undeclared identifier is reported only once
send_pipelined_http.c:30: error: for each function it appears in.)
send_pipelined_http.c:30: error: `CURLE_OK' undeclared (first use in this function)
send_pipelined_http.c:31: error: `stderr' undeclared (first use in this function)
send_pipelined_http.c:31: error: `func' undeclared (first use in this function)
send_pipelined_http.c: In function `main':
send_pipelined_http.c:76: error: `CURLM' undeclared (first use in this function)
send_pipelined_http.c:76: error: `m_curl' undeclared (first use in this function)
send_pipelined_http.c:77: error: `CURLMcode' undeclared (first use in this function)
send_pipelined_http.c:77: error: parse error before "res"
send_pipelined_http.c:80: error: `CURLMOPT_PIPELINING' undeclared (first use in this function)
send_pipelined_http.c:82: error: `CURL' undeclared (first use in this function)
send_pipelined_http.c:82: error: `curl' undeclared (first use in this function)
send_pipelined_http.c:89: error: `res' undeclared (first use in this function)
send_pipelined_http.c:89: error: `CURLOPT_URL' undeclared (first use in this function)
send_pipelined_http.c:92: error: `CURLOPT_FOLLOWLOCATION' undeclared (first use in this function)
Which is your OS?
DeleteRequests are not pipelined.
ReplyDeleteI have tested it and from the capture you can see that it is pipelined. Can you share your capture?
DeleteHi! How can I check that it is realy pipelined? How to get capture like yours?
DeleteYou can do a tcpdump and capture the packets.
DeleteHi Ahamed,
DeleteThank you for sharing your code. I also see that requests are not pipelined (one http get per socket). Here is a capture:
13:54:06.299436 IP 1.1.1.2.40010 > 1.1.1.1.http: Flags [S], seq 866784912, win 42340, options [mss 1460,sackOK,TS val 191092763 ecr 0,nop,wscale 11], length 0
13:54:06.299470 IP 1.1.1.1.http > 1.1.1.2.40010: Flags [S.], seq 2231060603, ack 866784913, win 65535, options [mss 1460,nop,wscale 3,nop,nop,TS val 4166 ecr 0], length 0
13:54:06.299488 IP 1.1.1.2.40010 > 1.1.1.1.http: Flags [.], ack 1, win 21, options [nop,nop,TS val 191092763 ecr 4166], length 0
13:54:06.299503 IP 1.1.1.2.40012 > 1.1.1.1.http: Flags [S], seq 2603313572, win 42340, options [mss 1460,sackOK,TS val 191092763 ecr 0,nop,wscale 11], length 0
13:54:06.299510 IP 1.1.1.1.http > 1.1.1.2.40010: Flags [.], ack 1, win 16425, options [nop,nop,TS val 4166 ecr 191092763], length 0
13:54:06.299522 IP 1.1.1.1.http > 1.1.1.2.40012: Flags [S.], seq 2240101762, ack 2603313573, win 65535, options [mss 1460,nop,wscale 3,nop,nop,TS val 4166 ecr 0], length 0
13:54:06.299532 IP 1.1.1.2.40012 > 1.1.1.1.http: Flags [.], ack 1, win 21, options [nop,nop,TS val 191092763 ecr 4166], length 0
13:54:06.299541 IP 1.1.1.2.40010 > 1.1.1.1.http: Flags [P.], seq 1:51, ack 1, win 21, options [nop,nop,TS val 191092763 ecr 4166], length 50: HTTP: GET /toto HTTP/1.1
13:54:06.299550 IP 1.1.1.1.http > 1.1.1.2.40012: Flags [.], ack 1, win 16425, options [nop,nop,TS val 4166 ecr 191092763], length 0
13:54:06.299561 IP 1.1.1.2.40012 > 1.1.1.1.http: Flags [P.], seq 1:51, ack 1, win 21, options [nop,nop,TS val 191092763 ecr 4166], length 50: HTTP: GET /toto HTTP/1.1
13:54:06.299565 IP 1.1.1.1.http > 1.1.1.2.40010: Flags [P.], seq 1:80, ack 51, win 16425, options [nop,nop,TS val 4166 ecr 191092763], length 79: HTTP: HTTP/1.1 200 OK
13:54:06.299571 IP 1.1.1.2.40010 > 1.1.1.1.http: Flags [.], ack 80, win 21, options [nop,nop,TS val 191092763 ecr 4166], length 0
13:54:06.299577 IP 1.1.1.1.http > 1.1.1.2.40012: Flags [P.], seq 1:80, ack 51, win 16425, options [nop,nop,TS val 4166 ecr 191092763], length 79: HTTP: HTTP/1.1 200 OK
13:54:06.299582 IP 1.1.1.2.40012 > 1.1.1.1.http: Flags [.], ack 80, win 21, options [nop,nop,TS val 191092763 ecr 4166], length 0
13:54:06.299620 IP 1.1.1.2.40010 > 1.1.1.1.http: Flags [F.], seq 51, ack 80, win 21, options [nop,nop,TS val 191092763 ecr 4166], length 0
13:54:06.299632 IP 1.1.1.2.40012 > 1.1.1.1.http: Flags [F.], seq 51, ack 80, win 21, options [nop,nop,TS val 191092763 ecr 4166], length 0
13:54:06.299640 IP 1.1.1.1.http > 1.1.1.2.40010: Flags [.], ack 52, win 16425, options [nop,nop,TS val 4166 ecr 191092763], length 0
13:54:06.299650 IP 1.1.1.1.http > 1.1.1.2.40010: Flags [F.], seq 80, ack 52, win 16425, options [nop,nop,TS val 4166 ecr 191092763], length 0
13:54:06.299661 IP 1.1.1.2.40010 > 1.1.1.1.http: Flags [.], ack 81, win 21, options [nop,nop,TS val 191092763 ecr 4166], length 0
13:54:06.299663 IP 1.1.1.1.http > 1.1.1.2.40012: Flags [.], ack 52, win 16425, options [nop,nop,TS val 4166 ecr 191092763], length 0
13:54:06.299668 IP 1.1.1.1.http > 1.1.1.2.40012: Flags [F.], seq 80, ack 52, win 16425, options [nop,nop,TS val 4166 ecr 191092763], length 0
13:54:06.299675 IP 1.1.1.2.40012 > 1.1.1.1.http: Flags [.], ack 81, win 21, options [nop,nop,TS val 191092763 ecr 4166], length 0
Hello, it has been a while since I worked on this. Let me know if you are still looking for a solution, i will re-validate this.
DeleteHi Ahamed, You can see that there are two connections opened. one with port :40010 and one with port:40012. There are 2 connection opened from client side.
DeleteIdea is both the request should go in same connection.
I compiled this code today on FreeBSD using the latest libCurl (7.60.0) and I see it making three connections to the server, and then looping forever in the "res = curl_multi_perform(m_curl, &ret);" loop with ret == 2.
ReplyDeleteMurf