strbuf: add strbuf_reencode helper
[gitweb.git] / http.c
diff --git a/http.c b/http.c
index 70eaa26e88cbcfa62e7d5e967b4e519432319210..84463dff3dc3c350abdc1cc1335a5fdddff5a1fe 100644 (file)
--- a/http.c
+++ b/http.c
@@ -880,6 +880,20 @@ int handle_curl_result(struct slot_results *results)
        }
 }
 
+int run_one_slot(struct active_request_slot *slot,
+                struct slot_results *results)
+{
+       slot->results = results;
+       if (!start_active_slot(slot)) {
+               snprintf(curl_errorstr, sizeof(curl_errorstr),
+                        "failed to start HTTP request");
+               return HTTP_START_FAILED;
+       }
+
+       run_active_slot(slot);
+       return handle_curl_result(results);
+}
+
 static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
 {
        char *ptr;
@@ -892,6 +906,80 @@ static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
        return ret;
 }
 
+/*
+ * Check for and extract a content-type parameter. "raw"
+ * should be positioned at the start of the potential
+ * parameter, with any whitespace already removed.
+ *
+ * "name" is the name of the parameter. The value is appended
+ * to "out".
+ */
+static int extract_param(const char *raw, const char *name,
+                        struct strbuf *out)
+{
+       size_t len = strlen(name);
+
+       if (strncasecmp(raw, name, len))
+               return -1;
+       raw += len;
+
+       if (*raw != '=')
+               return -1;
+       raw++;
+
+       while (*raw && !isspace(*raw))
+               strbuf_addch(out, *raw++);
+       return 0;
+}
+
+/*
+ * Extract a normalized version of the content type, with any
+ * spaces suppressed, all letters lowercased, and no trailing ";"
+ * or parameters.
+ *
+ * Note that we will silently remove even invalid whitespace. For
+ * example, "text / plain" is specifically forbidden by RFC 2616,
+ * but "text/plain" is the only reasonable output, and this keeps
+ * our code simple.
+ *
+ * If the "charset" argument is not NULL, store the value of any
+ * charset parameter there.
+ *
+ * Example:
+ *   "TEXT/PLAIN; charset=utf-8" -> "text/plain", "utf-8"
+ *   "text / plain" -> "text/plain"
+ */
+static void extract_content_type(struct strbuf *raw, struct strbuf *type,
+                                struct strbuf *charset)
+{
+       const char *p;
+
+       strbuf_reset(type);
+       strbuf_grow(type, raw->len);
+       for (p = raw->buf; *p; p++) {
+               if (isspace(*p))
+                       continue;
+               if (*p == ';') {
+                       p++;
+                       break;
+               }
+               strbuf_addch(type, tolower(*p));
+       }
+
+       if (!charset)
+               return;
+
+       strbuf_reset(charset);
+       while (*p) {
+               while (isspace(*p))
+                       p++;
+               if (!extract_param(p, "charset", charset))
+                       return;
+               while (*p && !isspace(*p))
+                       p++;
+       }
+}
+
 /* http_request() targets */
 #define HTTP_REQUEST_STRBUF    0
 #define HTTP_REQUEST_FILE      1
@@ -907,7 +995,6 @@ static int http_request(const char *url,
        int ret;
 
        slot = get_active_slot();
-       slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
 
        if (result == NULL) {
@@ -942,18 +1029,15 @@ static int http_request(const char *url,
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "gzip");
 
-       if (start_active_slot(slot)) {
-               run_active_slot(slot);
-               ret = handle_curl_result(&results);
-       } else {
-               snprintf(curl_errorstr, sizeof(curl_errorstr),
-                        "failed to start HTTP request");
-               ret = HTTP_START_FAILED;
-       }
+       ret = run_one_slot(slot, &results);
 
-       if (options && options->content_type)
-               curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE,
-                               options->content_type);
+       if (options && options->content_type) {
+               struct strbuf raw = STRBUF_INIT;
+               curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE, &raw);
+               extract_content_type(&raw, options->content_type,
+                                    options->charset);
+               strbuf_release(&raw);
+       }
 
        if (options && options->effective_url)
                curlinfo_strbuf(slot->curl, CURLINFO_EFFECTIVE_URL,
@@ -1384,7 +1468,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
        unsigned char *sha1)
 {
        char *hex = sha1_to_hex(sha1);
-       char *filename;
+       const char *filename;
        char prevfile[PATH_MAX];
        int prevlocal;
        char prev_buf[PREV_BUF_SIZE];