Index: uspace/app/download/Makefile
===================================================================
--- uspace/app/download/Makefile	(revision ef2ecece13b2062d2c267ab4834211465b310214)
+++ uspace/app/download/Makefile	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
@@ -29,5 +29,5 @@
 USPACE_PREFIX = ../..
 LIBS = $(LIBHTTP_PREFIX)/libhttp.a $(LIBURI_PREFIX)/liburi.a
-EXTRA_CFLAGS = -I$(LIBHTTP_PREFIX) -I$(LIBURI_PREFIX)
+EXTRA_CFLAGS = -I$(LIBHTTP_PREFIX)/include -I$(LIBURI_PREFIX)
 DEFS = -DRELEASE=$(RELEASE)
 BINARY = download
Index: uspace/app/download/main.c
===================================================================
--- uspace/app/download/main.c	(revision ef2ecece13b2062d2c267ab4834211465b310214)
+++ uspace/app/download/main.c	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
@@ -47,5 +47,5 @@
 #include <net/socket.h>
 
-#include <http.h>
+#include <http/http.h>
 #include <uri.h>
 
@@ -132,39 +132,32 @@
 		fprintf(stderr, "Failed creating request\n");
 		uri_destroy(uri);
-		free(server_path);
 		return 3;
 	}
 	
-	http_header_t *header_host = http_header_create("Host", uri->host);
-	if (header_host == NULL) {
-		fprintf(stderr, "Failed creating Host header\n");
-		uri_destroy(uri);
-		free(server_path);
-		return 3;
-	}
-	list_append(&header_host->link, &req->headers);
-	
-	http_header_t *header_ua = http_header_create("User-Agent", USER_AGENT);
-	if (header_ua == NULL) {
-		fprintf(stderr, "Failed creating User-Agent header\n");
-		uri_destroy(uri);
-		free(server_path);
-		return 3;
-	}
-	list_append(&header_ua->link, &req->headers);
+	int rc = http_headers_append(&req->headers, "Host", uri->host);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed setting Host header: %s\n", str_error(rc));
+		uri_destroy(uri);
+		return rc;
+	}
+	
+	rc = http_headers_append(&req->headers, "User-Agent", USER_AGENT);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed creating User-Agent header: %s\n", str_error(rc));
+		uri_destroy(uri);
+		return rc;
+	}
 	
 	http_t *http = http_create(uri->host, port);
 	if (http == NULL) {
 		uri_destroy(uri);
-		free(server_path);
 		fprintf(stderr, "Failed creating HTTP object\n");
 		return 3;
 	}
 	
-	int rc = http_connect(http);
+	rc = http_connect(http);
 	if (rc != EOK) {
 		fprintf(stderr, "Failed connecting: %s\n", str_error(rc));
 		uri_destroy(uri);
-		free(server_path);
 		return rc;
 	}
@@ -174,14 +167,13 @@
 		fprintf(stderr, "Failed sending request: %s\n", str_error(rc));
 		uri_destroy(uri);
-		free(server_path);
 		return rc;
 	}
 	
 	http_response_t *response = NULL;
-	rc = http_receive_response(http, &response);
+	rc = http_receive_response(&http->recv_buffer, &response, 16 * 1024,
+	    100);
 	if (rc != EOK) {
 		fprintf(stderr, "Failed receiving response: %s\n", str_error(rc));
 		uri_destroy(uri);
-		free(server_path);
 		return rc;
 	}
@@ -197,10 +189,9 @@
 			fprintf(stderr, "Failed allocating buffer\n)");
 			uri_destroy(uri);
-			free(server_path);
 			return ENOMEM;
 		}
 		
 		int body_size;
-		while ((body_size = http_receive_body(http, buf, buf_size)) > 0) {
+		while ((body_size = recv_buffer(&http->recv_buffer, buf, buf_size)) > 0) {
 			fwrite(buf, 1, body_size, stdout);
 		}
@@ -212,5 +203,4 @@
 	
 	uri_destroy(uri);
-	free(server_path);
 	return EOK;
 }
Index: uspace/lib/http/Makefile
===================================================================
--- uspace/lib/http/Makefile	(revision ef2ecece13b2062d2c267ab4834211465b310214)
+++ uspace/lib/http/Makefile	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
@@ -31,8 +31,12 @@
 SLIBRARY = libhttp.so.0.0
 LSONAME = libhttp.so0
-#EXTRA_CFLAGS += 
+EXTRA_CFLAGS += -Iinclude
 
 SOURCES = \
-	http.c
+	src/http.c \
+	src/headers.c \
+	src/request.c \
+	src/response.c \
+	src/receive-buffer.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: pace/lib/http/http.c
===================================================================
--- uspace/lib/http/http.c	(revision ef2ecece13b2062d2c267ab4834211465b310214)
+++ 	(revision )
@@ -1,525 +1,0 @@
-/*
- * 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
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <str.h>
-#include <macros.h>
-
-#include <net/socket.h>
-#include <inet/dnsr.h>
-
-#include "http.h"
-
-#define HTTP_METHOD_LINE "%s %s HTTP/1.1\r\n"
-#define HTTP_HEADER_LINE "%s: %s\r\n"
-#define HTTP_REQUEST_LINE "\r\n"
-
-static char *cut_str(const char *start, const char *end)
-{
-	size_t size = end - start;
-	return str_ndup(start, size);
-}
-
-static void recv_reset(http_t *http)
-{
-	http->recv_buffer_in = 0;
-	http->recv_buffer_out = 0;
-}
-
-/** Receive one character (with buffering) */
-static int recv_char(http_t *http, char *c, bool consume)
-{
-	if (http->recv_buffer_out == http->recv_buffer_in) {
-		recv_reset(http);
-		
-		ssize_t rc = recv(http->conn_sd, http->recv_buffer, http->buffer_size, 0);
-		if (rc <= 0)
-			return rc;
-		
-		http->recv_buffer_in = rc;
-	}
-	
-	*c = http->recv_buffer[http->recv_buffer_out];
-	if (consume)
-		http->recv_buffer_out++;
-	return EOK;
-}
-
-static ssize_t recv_buffer(http_t *http, char *buf, size_t buf_size)
-{
-	/* Flush any buffered data*/
-	if (http->recv_buffer_out != http->recv_buffer_in) {
-		size_t size = min(http->recv_buffer_in - http->recv_buffer_out, buf_size);
-		memcpy(buf, http->recv_buffer + http->recv_buffer_out, size);
-		http->recv_buffer_out += size;
-		return size;
-	}
-	
-	return recv(http->conn_sd, buf, buf_size, 0);
-}
-
-/** Receive a character and if it is c, discard it from input buffer */
-static int recv_discard(http_t *http, char discard)
-{
-	char c = 0;
-	int rc = recv_char(http, &c, false);
-	if (rc != EOK)
-		return rc;
-	if (c != discard)
-		return EOK;
-	return recv_char(http, &c, true);
-}
-
-/* Receive a single line */
-static ssize_t recv_line(http_t *http, char *line, size_t size)
-{
-	size_t written = 0;
-	
-	while (written < size) {
-		char c = 0;
-		int rc = recv_char(http, &c, true);
-		if (rc != EOK)
-			return rc;
-		if (c == '\n') {
-			recv_discard(http, '\r');
-			line[written++] = 0;
-			return written;
-		}
-		else if (c == '\r') {
-			recv_discard(http, '\n');
-			line[written++] = 0;
-			return written;
-		}
-		line[written++] = c;
-	}
-	
-	return ELIMIT;
-}
-
-http_t *http_create(const char *host, uint16_t port)
-{
-	http_t *http = malloc(sizeof(http_t));
-	if (http == NULL)
-		return NULL;
-	
-	http->host = str_dup(host);
-	if (http->host == NULL) {
-		free(http);
-		return NULL;
-	}
-	http->port = port;
-	
-	http->buffer_size = 4096;
-	http->recv_buffer = malloc(http->buffer_size);
-	if (http->recv_buffer == NULL) {
-		free(http->host);
-		free(http);
-		return NULL;
-	}
-	
-	return http;
-}
-
-int http_connect(http_t *http)
-{
-	if (http->connected)
-		return EBUSY;
-	
-	/* Interpret as address */
-	int rc = inet_addr_parse(http->host, &http->addr);
-	
-	if (rc != EOK) {
-		/* Interpret as a host name */
-		dnsr_hostinfo_t *hinfo = NULL;
-		rc = dnsr_name2host(http->host, &hinfo, ip_any);
-		
-		if (rc != EOK)
-			return rc;
-		
-		http->addr = hinfo->addr;
-		dnsr_hostinfo_destroy(hinfo);
-	}
-	
-	struct sockaddr *saddr;
-	socklen_t saddrlen;
-	
-	rc = inet_addr_sockaddr(&http->addr, http->port, &saddr, &saddrlen);
-	if (rc != EOK) {
-		assert(rc == ENOMEM);
-		return ENOMEM;
-	}
-	
-	http->conn_sd = socket(saddr->sa_family, SOCK_STREAM, 0);
-	if (http->conn_sd < 0)
-		return http->conn_sd;
-	
-	rc = connect(http->conn_sd, saddr, saddrlen);
-	free(saddr);
-	
-	return rc;
-}
-
-http_header_t *http_header_create(const char *name, const char *value)
-{
-	char *dname = str_dup(name);
-	if (dname == NULL)
-		return NULL;
-	
-	char *dvalue = str_dup(value);
-	if (dvalue == NULL) {
-		free(dname);
-		return NULL;
-	}
-
-	return http_header_create_no_copy(dname, dvalue);
-}
-
-http_header_t *http_header_create_no_copy(char *name, char *value)
-{
-	http_header_t *header = malloc(sizeof(http_header_t));
-	if (header == NULL)
-		return NULL;
-	
-	link_initialize(&header->link);
-	header->name = name;
-	header->value = value;
-	
-	return header;
-}
-
-void http_header_destroy(http_header_t *header)
-{
-	free(header->name);
-	free(header->value);
-	free(header);
-}
-
-http_request_t *http_request_create(const char *method, const char *path)
-{
-	http_request_t *req = malloc(sizeof(http_request_t));
-	if (req == NULL)
-		return NULL;
-	
-	req->method = str_dup(method);
-	if (req->method == NULL) {
-		free(req);
-		return NULL;
-	}
-	
-	req->path = str_dup(path);
-	if (req->path == NULL) {
-		free(req->method);
-		free(req);
-		return NULL;
-	}
-	
-	list_initialize(&req->headers);
-	
-	return req;
-}
-
-void http_request_destroy(http_request_t *req)
-{
-	free(req->method);
-	free(req->path);
-	link_t *link = req->headers.head.next;
-	while (link != &req->headers.head) {
-		link_t *next = link->next;
-		http_header_t *header = list_get_instance(link, http_header_t, link);
-		http_header_destroy(header);
-		link = next;
-	}
-	free(req);
-}
-
-static ssize_t http_encode_method(char *buf, size_t buf_size,
-    const char *method, const char *path)
-{
-	if (buf == NULL) {
-		return printf_size(HTTP_METHOD_LINE, method, path);
-	}
-	else {
-		return snprintf(buf, buf_size, HTTP_METHOD_LINE, method, path);
-	}
-}
-
-static ssize_t http_encode_header(char *buf, size_t buf_size,
-    http_header_t *header)
-{
-	/* TODO properly split long header values */
-	if (buf == NULL) {
-		return printf_size(HTTP_HEADER_LINE, header->name, header->value);
-	}
-	else {
-		return snprintf(buf, buf_size,
-		    HTTP_HEADER_LINE, header->name, header->value);
-	}
-}
-
-int http_request_format(http_request_t *req, char **out_buf,
-    size_t *out_buf_size)
-{
-	/* Compute the size of the request */
-	ssize_t meth_size = http_encode_method(NULL, 0, req->method, req->path);
-	if (meth_size < 0)
-		return meth_size;
-	size_t size = meth_size;
-	
-	list_foreach(req->headers, link, http_header_t, header) {
-		ssize_t header_size = http_encode_header(NULL, 0, header);
-		if (header_size < 0)
-			return header_size;
-		size += header_size;
-	}
-	size += str_length(HTTP_REQUEST_LINE);
-	
-	char *buf = malloc(size);
-	if (buf == NULL)
-		return ENOMEM;
-	
-	char *pos = buf;
-	size_t pos_size = size;
-	ssize_t written = http_encode_method(pos, pos_size, req->method, req->path);
-	if (written < 0) {
-		free(buf);
-		return written;
-	}
-	pos += written;
-	pos_size -= written;
-	
-	list_foreach(req->headers, link, http_header_t, header) {
-		written = http_encode_header(pos, pos_size, header);
-		if (written < 0) {
-			free(buf);
-			return written;
-		}
-		pos += written;
-		pos_size -= written;
-	}
-	
-	size_t rlsize = str_size(HTTP_REQUEST_LINE);
-	memcpy(pos, HTTP_REQUEST_LINE, rlsize);
-	pos_size -= rlsize;
-	assert(pos_size == 0);
-	
-	*out_buf = buf;
-	*out_buf_size = size;
-	return EOK;
-}
-
-int http_send_request(http_t *http, http_request_t *req)
-{
-	char *buf;
-	size_t buf_size;
-	
-	int rc = http_request_format(req, &buf, &buf_size);
-	if (rc != EOK)
-		return rc;
-	
-	rc = send(http->conn_sd, buf, buf_size, 0);
-	free(buf);
-	
-	return rc;
-}
-
-int http_parse_status(const char *line, http_version_t *out_version,
-    uint16_t *out_status, char **out_message)
-{
-	http_version_t version;
-	uint16_t status;
-	char *message = NULL;
-	
-	if (!str_test_prefix(line, "HTTP/"))
-		return EINVAL;
-	
-	const char *pos_version = line + 5;
-	const char *pos = pos_version;
-	
-	int rc = str_uint8_t(pos_version, &pos, 10, false, &version.major);
-	if (rc != EOK)
-		return rc;
-	if (*pos != '.')
-		return EINVAL;
-	pos++;
-	
-	pos_version = pos;
-	rc = str_uint8_t(pos_version, &pos, 10, false, &version.minor);
-	if (rc != EOK)
-		return rc;
-	if (*pos != ' ')
-		return EINVAL;
-	pos++;
-	
-	const char *pos_status = pos;
-	rc = str_uint16_t(pos_status, &pos, 10, false, &status);
-	if (rc != EOK)
-		return rc;
-	if (*pos != ' ')
-		return EINVAL;
-	pos++;
-	
-	if (out_message) {
-		message = str_dup(pos);
-		if (message == NULL)
-			return ENOMEM;
-	}
-	
-	if (out_version)
-		*out_version = version;
-	if (out_status)
-		*out_status = status;
-	if (out_message)
-		*out_message = message;
-	return EOK;
-}
-
-int http_parse_header(const char *line, char **out_name, char **out_value)
-{
-	const char *pos = line;
-	while (*pos != 0 && *pos != ':') pos++;
-	if (*pos != ':')
-		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) {
-		free(name);
-		return ENOMEM;
-	}
-	
-	*out_name = name;
-	*out_value = value;
-	
-	return EOK;
-}
-
-int http_receive_response(http_t *http, http_response_t **out_response)
-{
-	http_response_t *resp = malloc(sizeof(http_response_t));
-	if (resp == NULL)
-		return ENOMEM;
-	memset(resp, 0, sizeof(http_response_t));
-	list_initialize(&resp->headers);
-	
-	char *line = malloc(http->buffer_size);
-	if (line == NULL) {
-		free(resp);
-		return ENOMEM;
-	}
-	
-	int rc = recv_line(http, line, http->buffer_size);
-	if (rc < 0)
-		goto error;
-	
-	rc = http_parse_status(line, &resp->version, &resp->status,
-	    &resp->message);
-	if (rc != EOK)
-		goto error;
-	
-	while (true) {
-		rc = recv_line(http, line, http->buffer_size);
-		if (rc < 0)
-			goto error;
-		if (*line == 0)
-			break;
-		
-		char *name = NULL;
-		char *value = NULL;
-		rc = http_parse_header(line, &name, &value);
-		if (rc != EOK)
-			goto error;
-		
-		http_header_t *header = http_header_create_no_copy(name, value);
-		if (header == NULL) {
-			free(name);
-			free(value);
-			rc = ENOMEM;
-			goto error;
-		}
-		
-		list_append(&header->link, &resp->headers);
-	}
-	
-	*out_response = resp;
-	
-	return EOK;
-error:
-	free(line);
-	http_response_destroy(resp);
-	return rc;
-}
-
-int http_receive_body(http_t *http, void *buf, size_t buf_size)
-{
-	return recv_buffer(http, buf, buf_size);
-}
-
-void http_response_destroy(http_response_t *resp)
-{
-	free(resp->message);
-	link_t *link = resp->headers.head.next;
-	while (link != &resp->headers.head) {
-		link_t *next = link->next;
-		http_header_t *header = list_get_instance(link, http_header_t, link);
-		http_header_destroy(header);
-		link = next;
-	}
-	free(resp);
-}
-
-int http_close(http_t *http)
-{
-	if (!http->connected)
-		return EINVAL;
-	
-	return closesocket(http->conn_sd);
-}
-
-void http_destroy(http_t *http)
-{
-	free(http->recv_buffer);
-	free(http);
-}
-
-/** @}
- */
Index: pace/lib/http/http.h
===================================================================
--- uspace/lib/http/http.h	(revision ef2ecece13b2062d2c267ab4834211465b310214)
+++ 	(revision )
@@ -1,102 +1,0 @@
-/*
- * 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_HTTP_H_
-#define HTTP_HTTP_H_
-
-#include <net/socket.h>
-#include <adt/list.h>
-#include <inet/addr.h>
-
-typedef struct {
-	char *host;
-	uint16_t port;
-	inet_addr_t addr;
-
-	bool connected;
-	int conn_sd;
-	
-	size_t buffer_size;
-	char *recv_buffer;
-	size_t recv_buffer_in;
-	size_t recv_buffer_out;
-} http_t;
-
-typedef struct {
-	uint8_t minor;
-	uint8_t major;
-} http_version_t;
-
-typedef struct {
-	link_t link;
-	char *name;
-	char *value;
-} http_header_t;
-
-typedef struct {
-	char *method;
-	char *path;
-	list_t headers;
-} http_request_t;
-
-typedef struct {
-	http_version_t version;
-	uint16_t status;
-	char *message;
-	list_t headers;
-} http_response_t;
-
-extern http_t *http_create(const char *, uint16_t);
-extern int http_connect(http_t *);
-extern http_header_t *http_header_create(const char *, const char *);
-extern http_header_t *http_header_create_no_copy(char *, char *);
-extern void http_header_destroy(http_header_t *);
-extern http_request_t *http_request_create(const char *, const char *);
-extern void http_request_destroy(http_request_t *);
-extern int http_request_format(http_request_t *, char **, size_t *);
-extern int http_send_request(http_t *, http_request_t *);
-extern int http_parse_status(const char *, http_version_t *, uint16_t *,
-    char **);
-extern int http_parse_header(const char *, char **, char **);
-extern int http_receive_response(http_t *, http_response_t **);
-extern int http_receive_body(http_t *, void *, size_t);
-extern void http_response_destroy(http_response_t *);
-extern int http_close(http_t *);
-extern void http_destroy(http_t *);
-
-#endif
-
-/** @}
- */
Index: uspace/lib/http/include/http/ctype.h
===================================================================
--- uspace/lib/http/include/http/ctype.h	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
+++ uspace/lib/http/include/http/ctype.h	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
@@ -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/include/http/errno.h
===================================================================
--- uspace/lib/http/include/http/errno.h	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
+++ uspace/lib/http/include/http/errno.h	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
@@ -0,0 +1,48 @@
+/*
+ * 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_ERRNO_H_
+#define HTTP_ERRNO_H_
+
+#include <errno.h>
+
+#define HTTP_EMULTIPLE_HEADERS -20001
+#define HTTP_EMISSING_HEADER -20002
+#define HTTP_EPARSE -20003
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/http/include/http/http.h
===================================================================
--- uspace/lib/http/include/http/http.h	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
+++ uspace/lib/http/include/http/http.h	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
@@ -0,0 +1,140 @@
+/*
+ * 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_HTTP_H_
+#define HTTP_HTTP_H_
+
+#include <net/socket.h>
+#include <adt/list.h>
+#include <inet/addr.h>
+
+#include "receive-buffer.h"
+
+typedef struct {
+	char *host;
+	uint16_t port;
+	inet_addr_t addr;
+
+	bool connected;
+	int conn_sd;
+	
+	size_t buffer_size;
+	receive_buffer_t recv_buffer;
+} http_t;
+
+typedef struct {
+	uint8_t minor;
+	uint8_t major;
+} http_version_t;
+
+typedef struct {
+	link_t link;
+	char *name;
+	char *value;
+} http_header_t;
+
+typedef struct {
+	list_t list;
+} http_headers_t;
+
+typedef struct {
+	char *method;
+	char *path;
+	http_headers_t headers;
+} http_request_t;
+
+typedef struct {
+	http_version_t version;
+	uint16_t status;
+	char *message;
+	http_headers_t headers;
+} http_response_t;
+
+extern http_t *http_create(const char *, uint16_t);
+extern int http_connect(http_t *);
+
+extern void http_header_init(http_header_t *);
+extern http_header_t *http_header_create(const char *, const char *);
+extern int http_header_receive_name(receive_buffer_t *, receive_buffer_mark_t *);
+extern int http_header_receive_value(receive_buffer_t *, receive_buffer_mark_t *,
+    receive_buffer_mark_t *);
+extern int http_header_receive(receive_buffer_t *, http_header_t *, size_t,
+    size_t *);
+extern void http_header_normalize_value(char *);
+extern bool http_header_name_match(const char *, const char *);
+ssize_t http_header_encode(http_header_t *, char *, size_t);
+extern void http_header_destroy(http_header_t *);
+
+extern void http_headers_init(http_headers_t *);
+extern int http_headers_find_single(http_headers_t *, const char *,
+    http_header_t **);
+extern int http_headers_append(http_headers_t *, const char *, const char *);
+extern int http_headers_set(http_headers_t *, const char *, const char *);
+extern int http_headers_get(http_headers_t *, const char *, char **);
+extern int http_headers_receive(receive_buffer_t *, http_headers_t *, size_t,
+    unsigned);
+extern void http_headers_clear(http_headers_t *);
+
+#define http_headers_foreach(headers, iter) \
+    list_foreach((headers).list, link, http_header_t, (iter))
+
+static inline void http_headers_remove(http_headers_t *headers,
+    http_header_t *header)
+{
+	list_remove(&header->link);
+}
+
+static inline void http_headers_append_header(http_headers_t *headers,
+    http_header_t *header)
+{
+	list_append(&header->link, &headers->list);
+}
+
+extern http_request_t *http_request_create(const char *, const char *);
+extern void http_request_destroy(http_request_t *);
+extern int http_request_format(http_request_t *, char **, size_t *);
+extern int http_send_request(http_t *, http_request_t *);
+extern int http_receive_status(receive_buffer_t *, http_version_t *, uint16_t *,
+    char **);
+extern int http_receive_response(receive_buffer_t *, http_response_t **,
+    size_t, unsigned);
+extern void http_response_destroy(http_response_t *);
+extern int http_close(http_t *);
+extern void http_destroy(http_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/http/include/http/receive-buffer.h
===================================================================
--- uspace/lib/http/include/http/receive-buffer.h	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
+++ uspace/lib/http/include/http/receive-buffer.h	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
@@ -0,0 +1,91 @@
+/*
+ * 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_RECEIVE_BUFFER_H_
+#define HTTP_RECEIVE_BUFFER_H_
+
+#include <adt/list.h>
+
+/** Receive data.
+ *
+ * @param client_data client data
+ * @param buf buffer to store the data
+ * @param buf_size buffer size
+ * @return number of bytes received or negative error code
+ */
+typedef ssize_t (*receive_func_t)(void *, void *, size_t);
+
+typedef struct {
+	size_t size;
+	char *buffer;
+	size_t in;
+	size_t out;
+	
+	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;
+
+typedef bool (*char_class_func_t)(char);
+
+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 ssize_t recv_discard(receive_buffer_t *, char);
+extern ssize_t recv_discard_str(receive_buffer_t *, const char *);
+extern ssize_t recv_while(receive_buffer_t *, char_class_func_t);
+extern ssize_t recv_eol(receive_buffer_t *);
+extern ssize_t recv_line(receive_buffer_t *, char *, size_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/http/src/headers.c
===================================================================
--- uspace/lib/http/src/headers.c	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
+++ uspace/lib/http/src/headers.c	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
@@ -0,0 +1,388 @@
+/*
+ * 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <macros.h>
+
+#include <http/http.h>
+#include <http/ctype.h>
+#include <http/errno.h>
+
+#define HTTP_HEADER_LINE "%s: %s\r\n"
+
+void http_header_init(http_header_t *header)
+{
+	link_initialize(&header->link);
+	header->name = NULL;
+	header->value = NULL;
+}
+
+http_header_t *http_header_create(const char *name, const char *value)
+{
+	http_header_t *header = malloc(sizeof(http_header_t));
+	if (header == NULL)
+		return NULL;
+	http_header_init(header);
+
+	header->name = str_dup(name);
+	if (header->name == NULL) {
+		free(header);
+		return NULL;
+	}
+	
+	header->value = str_dup(value);
+	if (header->value == NULL) {
+		free(header->name);
+		free(header);
+		return NULL;
+	}
+
+	return header;
+}
+
+void http_header_destroy(http_header_t *header)
+{
+	free(header->name);
+	free(header->value);
+	free(header);
+}
+
+ssize_t http_header_encode(http_header_t *header, char *buf, size_t buf_size)
+{
+	/* TODO properly split long header values */
+	if (buf == NULL) {
+		return printf_size(HTTP_HEADER_LINE, header->name, header->value);
+	}
+	else {
+		return snprintf(buf, buf_size,
+		    HTTP_HEADER_LINE, header->name, header->value);
+	}
+}
+
+int http_header_receive_name(receive_buffer_t *rb,
+    receive_buffer_mark_t *name_end)
+{
+	char c = 0;
+	do {
+		if (name_end)
+			recv_mark_update(rb, name_end);
+		
+		int rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			return rc;
+	} while (is_token(c));
+	
+	if (c != ':')
+		return EINVAL;
+	
+	return EOK;
+}
+
+int http_header_receive_value(receive_buffer_t *rb,
+    receive_buffer_mark_t *value_start, receive_buffer_mark_t *value_end)
+{
+	int rc = EOK;
+	char c = 0;
+	
+	/* Ignore any inline LWS */
+	while (true) {
+		if (value_start)
+			recv_mark_update(rb, value_start);
+		
+		rc = recv_char(rb, &c, false);
+		if (rc != EOK)
+			return rc;
+		
+		if (c != ' ' && c != '\t')
+			break;
+		
+		rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			return rc;
+	}
+	
+	while (true) {
+		recv_mark_update(rb, value_end);
+		
+		rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			return rc;
+		
+		if (c != '\r' && c != '\n')
+			continue;
+		
+		rc = recv_discard(rb, (c == '\r' ? '\n' : '\r'));
+		if (rc < 0)
+			return rc;
+		
+		rc = recv_char(rb, &c, false);
+		if (rc != EOK)
+			return rc;
+		
+		if (c != ' ' && c != '\t')
+			break;
+		
+		/* Ignore the char */
+		rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			return rc;
+	}
+	
+	return EOK;
+}
+
+int http_header_receive(receive_buffer_t *rb, http_header_t *header,
+    size_t size_limit, size_t *out_bytes_used)
+{
+	receive_buffer_mark_t mark_start;
+	receive_buffer_mark_t mark_end;
+	
+	recv_mark(rb, &mark_start);
+	recv_mark(rb, &mark_end);
+	
+	int rc = http_header_receive_name(rb, &mark_end);
+	if (rc != EOK)
+		goto end;
+	
+	size_t name_size = mark_end.offset - mark_start.offset;
+	if (size_limit > 0 && name_size > size_limit) {
+		rc = ELIMIT;
+		goto end;
+	}
+	
+	char *name = NULL;
+	rc = recv_cut_str(rb, &mark_start, &mark_end, &name);
+	if (rc != EOK)
+		goto end;
+	
+	rc = http_header_receive_value(rb, &mark_start, &mark_end);
+	if (rc != EOK)
+		goto end_with_name;
+	
+	size_t value_size = mark_end.offset - mark_start.offset;
+	if (size_limit > 0 && (name_size + value_size) > size_limit) {
+		rc = ELIMIT;
+		goto end_with_name;
+	}
+	
+	char *value = NULL;
+	rc = recv_cut_str(rb, &mark_start, &mark_end, &value);
+	if (rc != EOK)
+		goto end_with_name;
+	
+	if (out_bytes_used)
+		*out_bytes_used = name_size + value_size;
+	
+	header->name = name;
+	header->value = value;
+	goto end;
+end_with_name:
+	free(name);
+end:
+	recv_unmark(rb, &mark_start);
+	recv_unmark(rb, &mark_end);
+	return rc;
+}
+
+/** 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;
+}
+
+/** Test if two header names are equivalent
+ *
+ */
+bool http_header_name_match(const char *name_a, const char *name_b)
+{
+	return stricmp(name_a, name_b) == 0;
+}
+
+void http_headers_init(http_headers_t *headers) {
+	list_initialize(&headers->list);
+}
+
+int http_headers_find_single(http_headers_t *headers, const char *name,
+    http_header_t **out_header)
+{
+	http_header_t *found = NULL;
+	http_headers_foreach(*headers, header) {
+		if (!http_header_name_match(header->name, name))
+			continue;
+		
+		if (found == NULL) {
+			found = header;
+		}
+		else {
+			return HTTP_EMULTIPLE_HEADERS;
+		}
+	}
+	
+	if (found == NULL)
+		return HTTP_EMISSING_HEADER;
+	
+	*out_header = found;
+	return EOK;
+}
+
+int http_headers_append(http_headers_t *headers, const char *name,
+    const char *value)
+{
+	http_header_t *header = http_header_create(name, value);
+	if (header == NULL)
+		return ENOMEM;
+	
+	http_headers_append_header(headers, header);
+	return EOK;
+}
+
+int http_headers_set(http_headers_t *headers, const char *name,
+    const char *value)
+{
+	http_header_t *header = NULL;
+	int rc = http_headers_find_single(headers, name, &header);
+	if (rc != EOK && rc != HTTP_EMISSING_HEADER)
+		return rc;
+	
+	if (rc == HTTP_EMISSING_HEADER)
+		return http_headers_append(headers, name, value);
+	
+	char *new_value = str_dup(value);
+	if (new_value == NULL)
+		return ENOMEM;
+	
+	free(header->value);
+	header->value = new_value;
+	return EOK;
+}
+
+int http_headers_get(http_headers_t *headers, const char *name, char **value)
+{
+	http_header_t *header = NULL;
+	int rc = http_headers_find_single(headers, name, &header);
+	if (rc != EOK)
+		return rc;
+	
+	*value = header->value;
+	return EOK;
+}
+
+int http_headers_receive(receive_buffer_t *rb, http_headers_t *headers,
+    size_t limit_alloc, unsigned limit_count)
+{
+	int rc = EOK;
+	unsigned added = 0;
+	
+	while (true) {
+		char c = 0;
+		rc = recv_char(rb, &c, false);
+		if (rc != EOK)
+			goto error;
+		
+		if (c == '\n' || c == '\r')
+			break;
+		
+		if (limit_count > 0 && added >= limit_count) {
+			rc = ELIMIT;
+			goto error;
+		}
+		
+		http_header_t *header = malloc(sizeof(http_header_t));
+		if (header == NULL) {
+			rc = ENOMEM;
+			goto error;
+		}
+		http_header_init(header);
+		
+		size_t header_size;
+		rc = http_header_receive(rb, header, limit_alloc, &header_size);
+		if (rc != EOK) {
+			free(header);
+			goto error;
+		}
+		limit_alloc -= header_size;
+		
+		http_headers_append_header(headers, header);
+		added++;
+	}
+	
+	return EOK;
+error:
+	while (added-- > 0) {
+		link_t *link = list_last(&headers->list);
+		http_header_t *header = list_get_instance(link, http_header_t, link);
+		http_headers_remove(headers, header);
+		http_header_destroy(header);
+	}
+	return rc;
+}
+
+void http_headers_clear(http_headers_t *headers)
+{
+	link_t *link = list_first(&headers->list);
+	while (link != NULL) {
+		link_t *next = list_next(link, &headers->list);
+		http_header_t *header = list_get_instance(link, http_header_t, link);
+		list_remove(link);
+		http_header_destroy(header);
+		link = next;
+	}
+}
+
+/** @}
+ */
Index: uspace/lib/http/src/http.c
===================================================================
--- uspace/lib/http/src/http.c	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
+++ uspace/lib/http/src/http.c	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
@@ -0,0 +1,131 @@
+/*
+ * 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <macros.h>
+
+#include <net/socket.h>
+#include <inet/dnsr.h>
+
+#include <http/http.h>
+#include <http/receive-buffer.h>
+
+static ssize_t http_receive(void *client_data, void *buf, size_t buf_size)
+{
+	http_t *http = client_data;
+	return recv(http->conn_sd, buf, buf_size, 0);
+}
+
+http_t *http_create(const char *host, uint16_t port)
+{
+	http_t *http = malloc(sizeof(http_t));
+	if (http == NULL)
+		return NULL;
+	
+	http->host = str_dup(host);
+	if (http->host == NULL) {
+		free(http);
+		return NULL;
+	}
+	http->port = port;
+	
+	http->buffer_size = 4096;
+	int rc = recv_buffer_init(&http->recv_buffer, http->buffer_size,
+	    http_receive, http);
+	if (rc != EOK) {
+		free(http);
+		return NULL;
+	}
+	
+	return http;
+}
+
+int http_connect(http_t *http)
+{
+	if (http->connected)
+		return EBUSY;
+	
+	/* Interpret as address */
+	int rc = inet_addr_parse(http->host, &http->addr);
+	
+	if (rc != EOK) {
+		/* Interpret as a host name */
+		dnsr_hostinfo_t *hinfo = NULL;
+		rc = dnsr_name2host(http->host, &hinfo, ip_any);
+		
+		if (rc != EOK)
+			return rc;
+		
+		http->addr = hinfo->addr;
+		dnsr_hostinfo_destroy(hinfo);
+	}
+	
+	struct sockaddr *saddr;
+	socklen_t saddrlen;
+	
+	rc = inet_addr_sockaddr(&http->addr, http->port, &saddr, &saddrlen);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		return ENOMEM;
+	}
+	
+	http->conn_sd = socket(saddr->sa_family, SOCK_STREAM, 0);
+	if (http->conn_sd < 0)
+		return http->conn_sd;
+	
+	rc = connect(http->conn_sd, saddr, saddrlen);
+	free(saddr);
+	
+	return rc;
+}
+
+int http_close(http_t *http)
+{
+	if (!http->connected)
+		return EINVAL;
+	
+	return closesocket(http->conn_sd);
+}
+
+void http_destroy(http_t *http)
+{
+	recv_buffer_fini(&http->recv_buffer);
+	free(http);
+}
+
+/** @}
+ */
Index: uspace/lib/http/src/receive-buffer.c
===================================================================
--- uspace/lib/http/src/receive-buffer.c	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
+++ uspace/lib/http/src/receive-buffer.c	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * 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
+ */
+
+#include <stdlib.h>
+#include <str.h>
+#include <errno.h>
+#include <macros.h>
+#include <adt/list.h>
+
+#include <http/receive-buffer.h>
+
+int recv_buffer_init(receive_buffer_t *rb, size_t buffer_size,
+    receive_func_t receive, void *client_data)
+{
+	rb->receive = receive;
+	rb->client_data = client_data;
+	
+	rb->in = 0;
+	rb->out = 0;
+	rb->size = buffer_size;
+	
+	list_initialize(&rb->marks);
+	
+	rb->buffer = malloc(buffer_size);
+	if (rb->buffer == NULL)
+		return ENOMEM;
+	return EOK;
+}
+
+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)
+{
+	free(rb->buffer);
+}
+
+void recv_reset(receive_buffer_t *rb)
+{
+	rb->in = 0;
+	rb->out = 0;
+}
+
+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) {
+		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->in, free);
+		if (rc <= 0)
+			return rc;
+		
+		rb->in = rc;
+	}
+	
+	*c = rb->buffer[rb->out];
+	if (consume)
+		rb->out++;
+	return EOK;
+}
+
+ssize_t recv_buffer(receive_buffer_t *rb, char *buf, size_t buf_size)
+{
+	/* Flush any buffered data*/
+	if (rb->out != rb->in) {
+		size_t size = min(rb->in - rb->out, buf_size);
+		memcpy(buf, rb->buffer + rb->out, size);
+		rb->out += size;
+		return size;
+	}
+	
+	return rb->receive(rb->client_data, buf, buf_size);
+}
+
+/** 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;
+	int rc = recv_char(rb, &c, false);
+	if (rc != EOK)
+		return rc;
+	if (c != discard)
+		return 0;
+	rc = recv_char(rb, &c, true);
+	if (rc != EOK)
+		return rc;
+	return 1;
+}
+
+/** Receive a prefix of constant string discard and return number of bytes read
+ * @return number of characters discarded or negative error code
+ */
+ssize_t recv_discard_str(receive_buffer_t *rb, const char *discard)
+{
+	size_t discarded = 0;
+	while (*discard) {
+		ssize_t rc = recv_discard(rb, *discard);
+		if (rc < 0)
+			return rc;
+		if (rc == 0)
+			break;
+		discarded++;
+		discard++;
+	}
+	return discarded;
+}
+
+ssize_t recv_while(receive_buffer_t *rb, char_class_func_t class)
+{
+	size_t received = 0;
+	
+	while (true) {
+		char c = 0;
+		int rc = recv_char(rb, &c, false);
+		if (rc != EOK)
+			return rc;
+		
+		if (!class(c))
+			break;
+		
+		rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			return rc;
+		
+		received++;
+	}
+	
+	return received;
+}
+
+/** 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;
+}
+
+/* Receive a single line */
+ssize_t recv_line(receive_buffer_t *rb, char *line, size_t size)
+{
+	size_t written = 0;
+	
+	while (written < size) {
+		char c = 0;
+		int rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			return rc;
+		if (c == '\n') {
+			recv_discard(rb, '\r');
+			line[written++] = 0;
+			return written;
+		}
+		else if (c == '\r') {
+			recv_discard(rb, '\n');
+			line[written++] = 0;
+			return written;
+		}
+		line[written++] = c;
+	}
+	
+	return ELIMIT;
+}
+
+/** @}
+ */
Index: uspace/lib/http/src/request.c
===================================================================
--- uspace/lib/http/src/request.c	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
+++ uspace/lib/http/src/request.c	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
@@ -0,0 +1,158 @@
+/*
+ * 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <macros.h>
+
+#include <net/socket.h>
+
+#include <http/http.h>
+
+#define HTTP_METHOD_LINE "%s %s HTTP/1.1\r\n"
+#define HTTP_REQUEST_LINE "\r\n"
+
+http_request_t *http_request_create(const char *method, const char *path)
+{
+	http_request_t *req = malloc(sizeof(http_request_t));
+	if (req == NULL)
+		return NULL;
+	
+	req->method = str_dup(method);
+	if (req->method == NULL) {
+		free(req);
+		return NULL;
+	}
+	
+	req->path = str_dup(path);
+	if (req->path == NULL) {
+		free(req->method);
+		free(req);
+		return NULL;
+	}
+	
+	http_headers_init(&req->headers);
+	
+	return req;
+}
+
+void http_request_destroy(http_request_t *req)
+{
+	free(req->method);
+	free(req->path);
+	http_headers_clear(&req->headers);
+	free(req);
+}
+
+static ssize_t http_encode_method(char *buf, size_t buf_size,
+    const char *method, const char *path)
+{
+	if (buf == NULL) {
+		return printf_size(HTTP_METHOD_LINE, method, path);
+	}
+	else {
+		return snprintf(buf, buf_size, HTTP_METHOD_LINE, method, path);
+	}
+}
+
+int http_request_format(http_request_t *req, char **out_buf,
+    size_t *out_buf_size)
+{
+	/* Compute the size of the request */
+	ssize_t meth_size = http_encode_method(NULL, 0, req->method, req->path);
+	if (meth_size < 0)
+		return meth_size;
+	size_t size = meth_size;
+	
+	http_headers_foreach(req->headers, header) {
+		ssize_t header_size = http_header_encode(header, NULL, 0);
+		if (header_size < 0)
+			return header_size;
+		size += header_size;
+	}
+	size += str_length(HTTP_REQUEST_LINE);
+	
+	char *buf = malloc(size);
+	if (buf == NULL)
+		return ENOMEM;
+	
+	char *pos = buf;
+	size_t pos_size = size;
+	ssize_t written = http_encode_method(pos, pos_size, req->method, req->path);
+	if (written < 0) {
+		free(buf);
+		return written;
+	}
+	pos += written;
+	pos_size -= written;
+	
+	http_headers_foreach(req->headers, header) {
+		written = http_header_encode(header, pos, pos_size);
+		if (written < 0) {
+			free(buf);
+			return written;
+		}
+		pos += written;
+		pos_size -= written;
+	}
+	
+	size_t rlsize = str_size(HTTP_REQUEST_LINE);
+	memcpy(pos, HTTP_REQUEST_LINE, rlsize);
+	pos_size -= rlsize;
+	assert(pos_size == 0);
+	
+	*out_buf = buf;
+	*out_buf_size = size;
+	return EOK;
+}
+
+int http_send_request(http_t *http, http_request_t *req)
+{
+	char *buf;
+	size_t buf_size;
+	
+	int rc = http_request_format(req, &buf, &buf_size);
+	if (rc != EOK)
+		return rc;
+	
+	rc = send(http->conn_sd, buf, buf_size, 0);
+	free(buf);
+	
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/http/src/response.c
===================================================================
--- uspace/lib/http/src/response.c	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
+++ uspace/lib/http/src/response.c	(revision 4f086417d58e6722abb07d1b3ecedde65654bcfb)
@@ -0,0 +1,228 @@
+/*
+ * 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <macros.h>
+
+#include <http/http.h>
+#include <http/errno.h>
+
+static bool is_digit(char c)
+{
+	return (c >= '0' && c <= '9');
+}
+
+static int receive_number(receive_buffer_t *rb, char **str)
+{
+	receive_buffer_mark_t start;
+	receive_buffer_mark_t end;
+	
+	recv_mark(rb, &start);
+	int rc = recv_while(rb, is_digit);
+	if (rc < 0) {
+		recv_unmark(rb, &start);
+		return rc;
+	}
+	recv_mark(rb, &end);
+	
+	rc = recv_cut_str(rb, &start, &end, str);
+	recv_unmark(rb, &start);
+	recv_unmark(rb, &end);
+	return rc;
+}
+
+static int receive_uint8_t(receive_buffer_t *rb, uint8_t *out_value)
+{
+	char *str = NULL;
+	int rc = receive_number(rb, &str);
+	
+	if (rc != EOK)
+		return rc;
+	
+	rc = str_uint8_t(str, NULL, 10, true, out_value);
+	free(str);
+	
+	return rc;
+}
+
+static int receive_uint16_t(receive_buffer_t *rb, uint16_t *out_value)
+{
+	char *str = NULL;
+	int rc = receive_number(rb, &str);
+	
+	if (rc != EOK)
+		return rc;
+	
+	rc = str_uint16_t(str, NULL, 10, true, out_value);
+	free(str);
+	
+	return rc;
+}
+
+static int expect(receive_buffer_t *rb, const char *expect)
+{
+	int rc = recv_discard_str(rb, expect);
+	if (rc < 0)
+		return rc;
+	if ((size_t) rc < str_length(expect))
+		return HTTP_EPARSE;
+	return EOK;
+}
+
+static bool is_not_newline(char c)
+{
+	return (c != '\n' && c != '\r');
+}
+
+int http_receive_status(receive_buffer_t *rb, http_version_t *out_version,
+    uint16_t *out_status, char **out_message)
+{
+	http_version_t version;
+	uint16_t status;
+	char *message = NULL;
+	
+	int rc = expect(rb, "HTTP/");
+	if (rc != EOK)
+		return rc;
+	
+	rc = receive_uint8_t(rb, &version.major);
+	if (rc != EOK)
+		return rc;
+	
+	rc = expect(rb, ".");
+	if (rc != EOK)
+		return rc;
+	
+	rc = receive_uint8_t(rb, &version.minor);
+	if (rc != EOK)
+		return rc;
+	
+	rc = expect(rb, " ");
+	if (rc != EOK)
+		return rc;
+	
+	rc = receive_uint16_t(rb, &status);
+	if (rc != EOK)
+		return rc;
+	
+	rc = expect(rb, " ");
+	if (rc != EOK)
+		return rc;
+	
+	receive_buffer_mark_t msg_start;
+	recv_mark(rb, &msg_start);
+	
+	rc = recv_while(rb, is_not_newline);
+	if (rc < 0) {
+		recv_unmark(rb, &msg_start);
+		return rc;
+	}
+	
+	receive_buffer_mark_t msg_end;
+	recv_mark(rb, &msg_end);
+	
+	if (out_message) {
+		rc = recv_cut_str(rb, &msg_start, &msg_end, &message);
+		if (rc != EOK) {
+			recv_unmark(rb, &msg_start);
+			recv_unmark(rb, &msg_end);
+			return rc;
+		}
+	}
+	
+	recv_unmark(rb, &msg_start);
+	recv_unmark(rb, &msg_end);
+	
+	rc = recv_eol(rb);
+	if (rc == 0)
+		rc = HTTP_EPARSE;
+	if (rc < 0) {
+		free(message);
+		return rc;
+	}
+	
+	if (out_version)
+		*out_version = version;
+	if (out_status)
+		*out_status = status;
+	if (out_message)
+		*out_message = message;
+	return EOK;
+}
+
+int http_receive_response(receive_buffer_t *rb, http_response_t **out_response,
+    size_t max_headers_size, unsigned max_headers_count)
+{
+	http_response_t *resp = malloc(sizeof(http_response_t));
+	if (resp == NULL)
+		return ENOMEM;
+	memset(resp, 0, sizeof(http_response_t));
+	http_headers_init(&resp->headers);
+	
+	int rc = http_receive_status(rb, &resp->version, &resp->status,
+	    &resp->message);
+	if (rc != EOK)
+		goto error;
+	
+	rc = http_headers_receive(rb, &resp->headers, max_headers_size,
+	    max_headers_count);
+	if (rc != EOK)
+		goto error;
+	
+	rc = recv_eol(rb);
+	if (rc == 0)
+		rc = HTTP_EPARSE;
+	if (rc < 0)
+		goto error;
+	
+	*out_response = resp;
+	
+	return EOK;
+error:
+	http_response_destroy(resp);
+	return rc;
+}
+
+void http_response_destroy(http_response_t *resp)
+{
+	free(resp->message);
+	http_headers_clear(&resp->headers);
+	free(resp);
+}
+
+/** @}
+ */
