int _intercept_open(const char *pathname) { int fd; CURLcode ret; intercept_t *obj; long status; RESOLVE(open64); RESOLVE(close); #define ERR_RETURN(err) do { if (fd >= 0) o_close(fd); errno = err; return -1; } while (0); #define CURL_SETOPT(option, value) if ((ret = curl_easy_setopt(obj->curl, option, value)) != CURLE_OK) ERR_RETURN(EACCES); if ((fd = o_open64("/dev/null", O_RDONLY, 0644)) < 0) ERR_RETURN(errno); DEBUGF("new fd=%d\n", fd); if (fd > HIGHEST_FD) ERR_RETURN(EMFILE); if (!(obj = calloc(1, sizeof(intercept_t)))) ERR_RETURN(ENOMEM); obj->refcount = 1; obj->url = strdup(pathname); if (!(obj->curl = curl_easy_init())) ERR_RETURN(ENOMEM); if (getenv("DEBUG")) CURL_SETOPT(CURLOPT_VERBOSE, 0x1); CURL_SETOPT(CURLOPT_URL, obj->url); CURL_SETOPT(CURLOPT_NOBODY, 0x1); CURL_SETOPT(CURLOPT_WRITEHEADER, obj); CURL_SETOPT(CURLOPT_HEADERFUNCTION, _curl_parse_header); CURL_SETOPT(CURLOPT_WRITEFUNCTION, _curl_output_null); if ((ret = curl_easy_perform(obj->curl)) != CURLE_OK) ERR_RETURN(EACCES); if ((ret = curl_easy_getinfo(obj->curl, CURLINFO_RESPONSE_CODE, &status)) != CURLE_OK || status != 200) ERR_RETURN(ENOENT); DEBUGF("curl_easy_perform succeeded\n"); if (!obj->size || !(obj->flags & FEAT_RANGE_SUPPORT)) { DEBUGF("unacceptable resource: size=%zu, flags=%d\n", obj->size, obj->flags); ERR_RETURN(ENXIO); } CURL_SETOPT(CURLOPT_WRITEHEADER, NULL); CURL_SETOPT(CURLOPT_HEADERFUNCTION, NULL); #undef CURL_SETOPT intercept[fd] = obj; errno = 0; return fd; }
size_t _intercept_read(int fd, void *buf, size_t count) { char range[512]; char *start, *finish; read_buffer_t read_buf; CURLcode ret; intercept_t *obj = intercept[fd]; off_t ra_size; long status; /* no point reading if we're already at the file boundary */ if (obj->offset >= obj->size) return 0; /* don't read over file boundary */ count = min(obj->size - obj->offset, count); /* satisfy from existing readahead buffer if possible */ if (obj->ra_buf && obj->offset >= obj->ra_fileoffset && obj->offset+count <= obj->ra_fileoffset+obj->ra_size) { off_t bo = obj->offset - obj->ra_fileoffset; memcpy(buf, obj->ra_buf+bo, count); obj->offset += count; return count; } /* work out how much we'd like to read ahead */ ra_size = min(obj->size - obj->offset, READAHEAD_MAX); /* format string for range request (inclusive) */ start = strdup(str_off_t(obj->offset)); finish = strdup(str_off_t(obj->offset+max(ra_size, count)-1)); snprintf(range, sizeof(range), "%s-%s", start, finish); free(start); free(finish); /* initialize structure used in _curl_output_buf */ read_buf.fd = fd; read_buf.buf = buf; read_buf.size = count; read_buf.offset = 0; /* throw old readahead buffer away, it didn't cut the mustard */ if (obj->ra_buf) { free(obj->ra_buf); obj->ra_buf = 0; } /* someone set us up the bomb */ if (ra_size > count && (obj->ra_buf = calloc(1, ra_size))) { obj->ra_size = ra_size; obj->ra_offset = 0; obj->ra_fileoffset = obj->offset; } #define CURL_SETOPT(option, value) if ((ret = curl_easy_setopt(obj->curl, option, value)) != CURLE_OK) return -1; /* setup curl and do request */ CURL_SETOPT(CURLOPT_HTTPGET, 0x1); CURL_SETOPT(CURLOPT_NOBODY, 0x0); CURL_SETOPT(CURLOPT_RANGE, range); CURL_SETOPT(CURLOPT_WRITEDATA, &read_buf); CURL_SETOPT(CURLOPT_WRITEFUNCTION, _curl_output_buf); if ((ret = curl_easy_perform(obj->curl)) != CURLE_OK) return -1; if ((ret = curl_easy_getinfo(obj->curl, CURLINFO_RESPONSE_CODE, &status)) != CURLE_OK || status != 206) return -1; #undef CURL_SETOPT DEBUGF("read succeeded. length=%s\n", str_off_t(read_buf.offset)); /* move offset with read */ obj->offset += read_buf.offset; return read_buf.offset; }
static void standard_request_handler(void *cls, struct SPDY_Request * request, uint8_t priority, const char *method, const char *path, const char *version, const char *host, const char *scheme, struct SPDY_NameValue * headers) { (void)cls; (void)priority; (void)host; (void)scheme; struct Proxy *proxy; int ret; struct URI *uri; struct SPDY_Session *session; PRINT_VERBOSE2("received request for '%s %s %s'\n", method, path, version); if(NULL == (proxy = malloc(sizeof(struct Proxy)))) DIE("No memory"); memset(proxy, 0, sizeof(struct Proxy)); session = SPDY_get_session_for_request(request); assert(NULL != session); proxy->session_alive = SPDY_get_cls_from_session(session); assert(NULL != proxy->session_alive); proxy->request = request; if(NULL == (proxy->headers = SPDY_name_value_create())) DIE("No memory"); if(glob_opt.transparent) { if(NULL != glob_opt.http_backend) //use always same host ret = asprintf(&(proxy->url),"%s://%s%s", scheme, glob_opt.http_backend, path); else //use host header ret = asprintf(&(proxy->url),"%s://%s%s", scheme, host, path); if(-1 == ret) DIE("No memory"); ret = parse_uri(&uri_preg, proxy->url, &uri); if(ret != 0) DIE("parsing built uri failed"); } else { ret = parse_uri(&uri_preg, path, &uri); PRINT_INFO2("path %s '%s' '%s'", path, uri->scheme, uri->host); if(ret != 0 || !strlen(uri->scheme) || !strlen(uri->host)) DIE("parsing received uri failed"); if(NULL != glob_opt.http_backend) //use backend host { ret = asprintf(&(proxy->url),"%s://%s%s", uri->scheme, glob_opt.http_backend, uri->path_and_more); if(-1 == ret) DIE("No memory"); } else //use request path if(NULL == (proxy->url = strdup(path))) DIE("No memory"); } free_uri(uri); PRINT_VERBOSE2("curl will request '%s'", proxy->url); SPDY_name_value_iterate(headers, &iterate_cb, proxy); if(NULL == (proxy->curl_handle = curl_easy_init())) { PRINT_INFO("curl_easy_init failed"); abort(); } if(glob_opt.curl_verbose) CURL_SETOPT(proxy->curl_handle, CURLOPT_VERBOSE, 1); CURL_SETOPT(proxy->curl_handle, CURLOPT_URL, proxy->url); if(glob_opt.http10) CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEFUNCTION, curl_write_cb); CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEDATA, proxy); CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERFUNCTION, curl_header_cb); CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERDATA, proxy); CURL_SETOPT(proxy->curl_handle, CURLOPT_PRIVATE, proxy); CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTPHEADER, proxy->curl_headers); CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); if(glob_opt.ipv4 && !glob_opt.ipv6) CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); else if(glob_opt.ipv6 && !glob_opt.ipv4) CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); if(CURLM_OK != (ret = curl_multi_add_handle(multi_handle, proxy->curl_handle))) { PRINT_INFO2("curl_multi_add_handle failed (%i)", ret); abort(); } //~5ms additional latency for calling this if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running)) && CURLM_CALL_MULTI_PERFORM != ret) { PRINT_INFO2("curl_multi_perform failed (%i)", ret); abort(); } call_curl_run = true; }