Index: uspace/lib/http/headers.c
===================================================================
--- uspace/lib/http/headers.c	(revision c17469e42264d4df053bb02adb05c2a365e8e922)
+++ uspace/lib/http/headers.c	(revision 3ce68b7bc50391278f778663dd78d5162fbda5d0)
@@ -40,12 +40,7 @@
 
 #include "http.h"
+#include "http-ctype.h"
 
 #define HTTP_HEADER_LINE "%s: %s\r\n"
-
-static char *cut_str(const char *start, const char *end)
-{
-	size_t size = end - start;
-	return str_ndup(start, size);
-}
 
 void http_header_init(http_header_t *header)
@@ -98,31 +93,158 @@
 }
 
-int http_header_parse(const char *line, http_header_t *header)
-{
-	const char *pos = line;
-	while (*pos != 0 && *pos != ':') pos++;
-	if (*pos != ':')
+int http_header_receive_name(receive_buffer_t *rb, char **out_name)
+{
+	receive_buffer_mark_t name_start;
+	receive_buffer_mark_t name_end;
+	
+	recv_mark(rb, &name_start);
+	recv_mark(rb, &name_end);
+	
+	char c = 0;
+	do {
+		recv_mark_update(rb, &name_end);
+		
+		int rc = recv_char(rb, &c, true);
+		if (rc != EOK) {
+			recv_unmark(rb, &name_start);
+			recv_unmark(rb, &name_end);
+			return rc;
+		}
+	} while (is_token(c));
+	
+	if (c != ':') {
+		recv_unmark(rb, &name_start);
+		recv_unmark(rb, &name_end);
 		return EINVAL;
-	
-	char *name = cut_str(line, pos);
-	if (name == NULL)
-		return ENOMEM;
-	
-	pos++;
-	
-	while (*pos == ' ') pos++;
-	
-	char *value = str_dup(pos);
-	if (value == NULL) {
+	}
+	
+	char *name = NULL;
+	int rc = recv_cut_str(rb, &name_start, &name_end, &name);
+	recv_unmark(rb, &name_start);
+	recv_unmark(rb, &name_end);
+	if (rc != EOK)
+		return rc;
+	
+	*out_name = name;
+	return EOK;
+}
+
+int http_header_receive_value(receive_buffer_t *rb, char **out_value)
+{
+	int rc = EOK;
+	char c = 0;
+	
+	receive_buffer_mark_t value_start;
+	recv_mark(rb, &value_start);
+	
+	/* Ignore any inline LWS */
+	while (true) {
+		recv_mark_update(rb, &value_start);
+		rc = recv_char(rb, &c, false);
+		if (rc != EOK)
+			goto error;
+		
+		if (c != ' ' && c != '\t')
+			break;
+		
+		rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			goto error;
+	}
+	
+	receive_buffer_mark_t value_end;
+	recv_mark(rb, &value_end);
+	
+	while (true) {
+		recv_mark_update(rb, &value_end);
+		
+		rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			goto error_end;
+		
+		if (c != '\r' && c != '\n')
+			continue;
+		
+		rc = recv_discard(rb, (c == '\r' ? '\n' : '\r'));
+		if (rc < 0)
+			goto error_end;
+		
+		rc = recv_char(rb, &c, false);
+		if (rc != EOK)
+			goto error_end;
+		
+		if (c != ' ' && c != '\t')
+			break;
+		
+		/* Ignore the char */
+		rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			goto error_end;
+	}
+	
+	char *value = NULL;
+	rc = recv_cut_str(rb, &value_start, &value_end, &value);
+	recv_unmark(rb, &value_start);
+	recv_unmark(rb, &value_end);
+	if (rc != EOK)
+		return rc;
+	
+	*out_value = value;
+	return EOK;
+error_end:
+	recv_unmark(rb, &value_end);
+error:
+	recv_unmark(rb, &value_start);
+	return rc;
+}
+
+int http_header_receive(receive_buffer_t *rb, http_header_t *header)
+{
+	char *name = NULL;
+	int rc = http_header_receive_name(rb, &name);
+	if (rc != EOK) {
+		printf("Failed receiving header name\n");
+		return rc;
+	}
+	
+	char *value = NULL;
+	rc = http_header_receive_value(rb, &value);
+	if (rc != EOK) {
 		free(name);
-		return ENOMEM;
+		return rc;
 	}
 	
 	header->name = name;
 	header->value = value;
-	
 	return EOK;
 }
 
+/** Normalize HTTP header value
+ *
+ * @see RFC2616 section 4.2
+ */
+void http_header_normalize_value(char *value)
+{
+	size_t read_index = 0;
+	size_t write_index = 0;
+	
+	while (is_lws(value[read_index])) read_index++;
+	
+	while (value[read_index] != 0) {
+		if (is_lws(value[read_index])) {
+			while (is_lws(value[read_index])) read_index++;
+			
+			if (value[read_index] != 0)
+				value[write_index++] = ' ';
+			
+			continue;
+		}
+		
+		value[write_index++] = value[read_index++];
+	}
+	
+	value[write_index] = 0;
+}
+
 /** @}
  */
Index: uspace/lib/http/http-ctype.h
===================================================================
--- uspace/lib/http/http-ctype.h	(revision 3ce68b7bc50391278f778663dd78d5162fbda5d0)
+++ uspace/lib/http/http-ctype.h	(revision 3ce68b7bc50391278f778663dd78d5162fbda5d0)
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup http
+ * @{
+ */
+/**
+ * @file
+ */
+
+#ifndef HTTP_CTYPE_H_
+#define HTTP_CTYPE_H_
+
+static inline bool is_separator(char c)
+{
+	switch (c) {
+	case '(':
+	case ')':
+	case '<':
+	case '>':
+	case '@':
+	case ',':
+	case ';':
+	case ':':
+	case '\\':
+	case '"':
+	case '/':
+	case '[':
+	case ']':
+	case '?':
+	case '=':
+	case '{':
+	case '}':
+	case ' ':
+	case '\t':
+		return true;
+	default:
+		return false;
+	}
+}
+
+static inline bool is_lws(char c)
+{
+	switch (c) {
+	case '\r':
+	case '\n':
+	case ' ':
+	case '\t':
+		return true;
+	default:
+		return false;
+	}
+}
+
+static inline bool is_ctl(char c)
+{
+	return c < 32 || c == 127;
+}
+
+static inline bool is_token(char c)
+{
+	return !is_separator(c) && !is_ctl(c);
+}
+
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/http/http.h
===================================================================
--- uspace/lib/http/http.h	(revision c17469e42264d4df053bb02adb05c2a365e8e922)
+++ uspace/lib/http/http.h	(revision 3ce68b7bc50391278f778663dd78d5162fbda5d0)
@@ -84,5 +84,8 @@
 extern void http_header_init(http_header_t *);
 extern http_header_t *http_header_create(const char *, const char *);
-extern int http_header_parse(const char *, http_header_t *);
+extern int http_header_receive_name(receive_buffer_t *, char **);
+extern int http_header_receive_value(receive_buffer_t *, char **);
+extern int http_header_receive(receive_buffer_t *, http_header_t *);
+extern void http_header_normalize_value(char *);
 ssize_t http_header_encode(http_header_t *, char *, size_t);
 extern void http_header_destroy(http_header_t *);
Index: uspace/lib/http/receive-buffer.c
===================================================================
--- uspace/lib/http/receive-buffer.c	(revision c17469e42264d4df053bb02adb05c2a365e8e922)
+++ uspace/lib/http/receive-buffer.c	(revision 3ce68b7bc50391278f778663dd78d5162fbda5d0)
@@ -39,4 +39,5 @@
 #include <errno.h>
 #include <macros.h>
+#include <adt/list.h>
 
 #include "receive-buffer.h"
@@ -51,4 +52,7 @@
 	rb->out = 0;
 	rb->size = buffer_size;
+	
+	list_initialize(&rb->marks);
+	
 	rb->buffer = malloc(buffer_size);
 	if (rb->buffer == NULL)
@@ -57,4 +61,20 @@
 }
 
+static ssize_t dummy_receive(void *unused, void *buf, size_t buf_size)
+{
+	return 0;
+}
+
+int recv_buffer_init_const(receive_buffer_t *rb, void *buf, size_t size)
+{
+	int rc = recv_buffer_init(rb, size, dummy_receive, NULL);
+	if (rc != EOK)
+		return rc;
+	
+	memcpy(rb->buffer, buf, size);
+	rb->in = size;
+	return EOK;
+}
+
 void recv_buffer_fini(receive_buffer_t *rb)
 {
@@ -68,11 +88,84 @@
 }
 
+void recv_mark(receive_buffer_t *rb, receive_buffer_mark_t *mark)
+{
+	link_initialize(&mark->link);
+	list_append(&mark->link, &rb->marks);
+	recv_mark_update(rb, mark);
+}
+
+void recv_unmark(receive_buffer_t *rb, receive_buffer_mark_t *mark)
+{
+	list_remove(&mark->link);
+}
+
+void recv_mark_update(receive_buffer_t *rb, receive_buffer_mark_t *mark)
+{
+	mark->offset = rb->out;
+}
+
+int recv_cut(receive_buffer_t *rb, receive_buffer_mark_t *a, receive_buffer_mark_t *b, void **out_buf, size_t *out_size)
+{
+	if (a->offset > b->offset)
+		return EINVAL;
+	
+	size_t size = b->offset - a->offset;
+	void *buf = malloc(size);
+	if (buf == NULL)
+		return ENOMEM;
+	
+	memcpy(buf, rb->buffer + a->offset, size);
+	*out_buf = buf;
+	*out_size = size;
+	return EOK;
+}
+
+int recv_cut_str(receive_buffer_t *rb, receive_buffer_mark_t *a, receive_buffer_mark_t *b, char **out_buf)
+{
+	if (a->offset > b->offset)
+		return EINVAL;
+	
+	size_t size = b->offset - a->offset;
+	char *buf = malloc(size + 1);
+	if (buf == NULL)
+		return ENOMEM;
+	
+	memcpy(buf, rb->buffer + a->offset, size);
+	buf[size] = 0;
+	for (size_t i = 0; i < size; i++) {
+		if (buf[i] == 0) {
+			free(buf);
+			return EIO;
+		}
+	}
+	*out_buf = buf;
+	return EOK;
+}
+
+
 /** Receive one character (with buffering) */
 int recv_char(receive_buffer_t *rb, char *c, bool consume)
 {
 	if (rb->out == rb->in) {
-		recv_reset(rb);
+		size_t free = rb->size - rb->in;
+		if (free == 0) {
+			size_t min_mark = rb->size;
+			list_foreach(rb->marks, link, receive_buffer_mark_t, mark) {
+				min_mark = min(min_mark, mark->offset);
+			}
+			
+			if (min_mark == 0)
+				return ELIMIT;
+			
+			size_t new_in = rb->in - min_mark;
+			memmove(rb->buffer, rb->buffer + min_mark, new_in);
+			rb->out = rb->in = new_in;
+			free = rb->size - rb->in;
+			list_foreach(rb->marks, link, receive_buffer_mark_t, mark) {
+				mark->offset -= min_mark;
+			}
+		}
 		
-		ssize_t rc = rb->receive(rb->client_data, rb->buffer, rb->size);
+		ssize_t rc = rb->receive(rb->client_data, rb->buffer + rb->in, free);
 		if (rc <= 0)
 			return rc;
@@ -100,6 +193,8 @@
 }
 
-/** Receive a character and if it is c, discard it from input buffer */
-int recv_discard(receive_buffer_t *rb, char discard)
+/** Receive a character and if it is c, discard it from input buffer
+ * @return number of characters discarded (0 or 1) or negative error code
+ */
+ssize_t recv_discard(receive_buffer_t *rb, char discard)
 {
 	char c = 0;
@@ -108,6 +203,35 @@
 		return rc;
 	if (c != discard)
-		return EOK;
-	return recv_char(rb, &c, true);
+		return 0;
+	rc = recv_char(rb, &c, true);
+	if (rc != EOK)
+		return rc;
+	return 1;
+}
+
+/** Receive an end of line, either CR, LF, CRLF or LFCR
+ *
+ * @return number of bytes read (0 if no newline is present in the stream)
+ *         or negative error code
+ */
+ssize_t recv_eol(receive_buffer_t *rb)
+{
+	char c = 0;
+	int rc = recv_char(rb, &c, false);
+	if (rc != EOK)
+		return rc;
+	
+	if (c != '\r' && c != '\n')
+		return 0;
+	
+	rc = recv_char(rb, &c, true);
+	if (rc != EOK)
+		return rc;
+	
+	ssize_t rc2 = recv_discard(rb, (c == '\r' ? '\n' : '\r'));
+	if (rc2 < 0)
+		return rc2;
+	
+	return 1 + rc2;
 }
 
Index: uspace/lib/http/receive-buffer.h
===================================================================
--- uspace/lib/http/receive-buffer.h	(revision c17469e42264d4df053bb02adb05c2a365e8e922)
+++ uspace/lib/http/receive-buffer.h	(revision 3ce68b7bc50391278f778663dd78d5162fbda5d0)
@@ -37,4 +37,5 @@
 #define HTTP_RECEIVE_BUFFER_H_
 
+#include <adt/list.h>
 
 /** Receive data.
@@ -55,12 +56,28 @@
 	void *client_data;
 	receive_func_t receive;
+	
+	list_t marks;
 } receive_buffer_t;
 
+typedef struct {
+	link_t link;
+	size_t offset;
+} receive_buffer_mark_t;
+
 extern int recv_buffer_init(receive_buffer_t *, size_t, receive_func_t, void *);
+extern int recv_buffer_init_const(receive_buffer_t *, void *, size_t);
 extern void recv_buffer_fini(receive_buffer_t *);
 extern void recv_reset(receive_buffer_t *);
+extern void recv_mark(receive_buffer_t *, receive_buffer_mark_t *);
+extern void recv_unmark(receive_buffer_t *, receive_buffer_mark_t *);
+extern void recv_mark_update(receive_buffer_t *, receive_buffer_mark_t *);
+extern int recv_cut(receive_buffer_t *, receive_buffer_mark_t *,
+    receive_buffer_mark_t *, void **, size_t *);
+extern int recv_cut_str(receive_buffer_t *, receive_buffer_mark_t *,
+    receive_buffer_mark_t *, char **);
 extern int recv_char(receive_buffer_t *, char *, bool);
 extern ssize_t recv_buffer(receive_buffer_t *, char *, size_t);
-extern int recv_discard(receive_buffer_t *, char);
+extern ssize_t recv_discard(receive_buffer_t *, char);
+extern ssize_t recv_eol(receive_buffer_t *);
 extern ssize_t recv_line(receive_buffer_t *, char *, size_t);
 
Index: uspace/lib/http/response.c
===================================================================
--- uspace/lib/http/response.c	(revision c17469e42264d4df053bb02adb05c2a365e8e922)
+++ uspace/lib/http/response.c	(revision 3ce68b7bc50391278f778663dd78d5162fbda5d0)
@@ -116,8 +116,10 @@
 	
 	while (true) {
-		rc = recv_line(&http->recv_buffer, line, http->buffer_size);
+		rc = recv_eol(&http->recv_buffer);
 		if (rc < 0)
 			goto error;
-		if (*line == 0)
+		
+		/* Empty line ends header part */
+		if (rc > 0)
 			break;
 		
@@ -129,7 +131,9 @@
 		http_header_init(header);
 		
-		rc = http_header_parse(line, header);
-		if (rc != EOK)
+		rc = http_header_receive(&http->recv_buffer, header);
+		if (rc != EOK) {
+			free(header);
 			goto error;
+		}
 		
 		list_append(&header->link, &resp->headers);
