+ establish_session:
+ if (use_proxy || !target.is_ftp) {
+ /*
+ * HTTP session
+ */
+ char *str;
+ int status;
+
+ /* Open socket to http server */
+ sfp = open_socket(lsa);
+
+ /* Send HTTP request */
+ if (use_proxy) {
+ fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
+ target.is_ftp ? "f" : "ht", target.host,
+ target.path);
+ } else {
+ if (opt & WGET_OPT_POST_DATA)
+ fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
+ else
+ fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
+ }
+
+ fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
+ target.host, user_agent);
+
+#if ENABLE_FEATURE_WGET_AUTHENTICATION
+ if (target.user) {
+ fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
+ base64enc_512(buf, target.user));
+ }
+ if (use_proxy && server.user) {
+ fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
+ base64enc_512(buf, server.user));
+ }
+#endif
+
+ if (beg_range)
+ fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ if (extra_headers)
+ fputs(extra_headers, sfp);
+
+ if (opt & WGET_OPT_POST_DATA) {
+ char *estr = URL_escape(post_data);
+ fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n");
+ fprintf(sfp, "Content-Length: %u\r\n" "\r\n" "%s",
+ (int) strlen(estr), estr);
+ /*fprintf(sfp, "Connection: Keep-Alive\r\n\r\n");*/
+ /*fprintf(sfp, "%s\r\n", estr);*/
+ free(estr);
+ } else
+#endif
+ { /* If "Connection:" is needed, document why */
+ fprintf(sfp, /* "Connection: close\r\n" */ "\r\n");
+ }
+
+ /*
+ * Retrieve HTTP response line and check for "200" status code.
+ */
+ read_response:
+ if (fgets(buf, sizeof(buf), sfp) == NULL)
+ bb_error_msg_and_die("no response from server");
+
+ str = buf;
+ str = skip_non_whitespace(str);
+ str = skip_whitespace(str);
+ // FIXME: no error check
+ // xatou wouldn't work: "200 OK"
+ status = atoi(str);
+ switch (status) {
+ case 0:
+ case 100:
+ while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
+ /* eat all remaining headers */;
+ goto read_response;
+ case 200:
+/*
+Response 204 doesn't say "null file", it says "metadata
+has changed but data didn't":
+
+"10.2.5 204 No Content
+The server has fulfilled the request but does not need to return
+an entity-body, and might want to return updated metainformation.
+The response MAY include new or updated metainformation in the form
+of entity-headers, which if present SHOULD be associated with
+the requested variant.
+
+If the client is a user agent, it SHOULD NOT change its document
+view from that which caused the request to be sent. This response
+is primarily intended to allow input for actions to take place
+without causing a change to the user agent's active document view,
+although any new or updated metainformation SHOULD be applied
+to the document currently in the user agent's active view.
+
+The 204 response MUST NOT include a message-body, and thus
+is always terminated by the first empty line after the header fields."
+
+However, in real world it was observed that some web servers
+(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
+*/
+ case 204:
+ break;
+ case 300: /* redirection */
+ case 301:
+ case 302:
+ case 303:
+ break;
+ case 206:
+ if (beg_range)
+ break;
+ /* fall through */
+ default:
+ bb_error_msg_and_die("server returned error: %s", sanitize_string(buf));
+ }
+
+ /*
+ * Retrieve HTTP headers.
+ */
+ while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
+ /* gethdr converted "FOO:" string to lowercase */
+ smalluint key;
+ /* strip trailing whitespace */
+ char *s = strchrnul(str, '\0') - 1;
+ while (s >= str && (*s == ' ' || *s == '\t')) {
+ *s = '\0';
+ s--;
+ }
+ key = index_in_strings(keywords, buf) + 1;
+ if (key == KEY_content_length) {
+ content_len = BB_STRTOOFF(str, NULL, 10);
+ if (errno || content_len < 0) {
+ bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str));
+ }
+ G.got_clen = 1;
+ continue;
+ }
+ if (key == KEY_transfer_encoding) {
+ if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
+ bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
+ G.chunked = G.got_clen = 1;
+ }
+ if (key == KEY_location && status >= 300) {
+ if (--redir_limit == 0)
+ bb_error_msg_and_die("too many redirections");
+ fclose(sfp);
+ G.got_clen = 0;
+ G.chunked = 0;
+ if (str[0] == '/')
+ /* free(target.allocated); */
+ target.path = /* target.allocated = */ xstrdup(str+1);
+ /* lsa stays the same: it's on the same server */
+ else {
+ parse_url(str, &target);
+ if (!use_proxy) {
+ server.host = target.host;
+ /* strip_ipv6_scope_id(target.host); - no! */
+ /* we assume remote never gives us IPv6 addr with scope id */
+ server.port = target.port;
+ free(lsa);
+ goto resolve_lsa;
+ } /* else: lsa stays the same: we use proxy */
+ }
+ goto establish_session;
+ }
+ }
+// if (status >= 300)
+// bb_error_msg_and_die("bad redirection (no Location: header from server)");
+
+ /* For HTTP, data is pumped over the same connection */
+ dfp = sfp;