Repository: goadesign/goa
Pull request #2575 (2020-06-06 05:26:53)

Cookies
This PR adds support for using HTTP cookies natively using the Goa DSL:

* Cookies defined in HTTP requests can be mapped to payload attributes.
* Cookies may also be written to HTTP responses by mapping result attributes.

When creating cookies in HTTP responses the design may also specify the value of cookies attributes such as path, domain, max-age etc.

This PR introduces the following DSL functions:

* [Cookie](https://pkg.go.dev/goa.design/goa/v3@v3.1.3/dsl?tab=doc#Cookie)
* [CookiePath](https://pkg.go.dev/goa.design/goa/v3@v3.1.3/dsl?tab=doc#CookiePath)
* [CookieDomain](https://pkg.go.dev/goa.design/goa/v3@v3.1.3/dsl?tab=doc#CookieDomain)
* [CookieMaxAge](https://pkg.go.dev/goa.design/goa/v3@v3.1.3/dsl?tab=doc#CookieMaxAge)
* [CookieSecure](https://pkg.go.dev/goa.design/goa/v3@v3.1.3/dsl?tab=doc#CookieSecure)
* [CookieHTTPOnly](https://pkg.go.dev/goa.design/goa/v3@v3.1.3/dsl?tab=doc#CookieHTTPOnly)



Patch:
diff --git a/dsl/http.go b/dsl/http.go
index 8758c465..8ee568fc 100644
--- a/dsl/http.go
+++ b/dsl/http.go
@@ -2,6 +2,7 @@ package dsl
 
 import (
 	"fmt"
+	"strconv"
 	"strings"
 
 	"goa.design/goa/v3/eval"
@@ -345,12 +346,13 @@ func route(method, path string) *expr.RouteExpr {
 // request or response type attribute with the same name by default.
 //
 // Header must appear in the API HTTP expression (to define request headers
-// common to all the API endpoints), a specific method HTTP expression (to
-// define request headers) or a Response expression (to define the response
-// headers). Header may also appear in a method GRPC expression (to define
-// headers sent in message metadata), or in a Response expression (to define
-// headers sent in result metadata). Finally Header may also appear in a Headers
-// expression.
+// common to all the API endpoints), a service HTTP expression (to define
+// request headers common to all the service endpoints) a specific method HTTP
+// expression (to define request headers) or a Response expression (to define
+// the response headers). Header may also appear in a method GRPC expression (to
+// define headers sent in message metadata), or in a Response expression (to
+// define headers sent in result metadata). Finally Header may also appear in a
+// Headers expression.
 //
 // Header accepts the same arguments as the Attribute function. The header name
 // may define a mapping between the attribute name and the HTTP header name when
@@ -387,6 +389,214 @@ func Header(name string, args ...interface{}) {
 	h.Remap()
 }
 
+// Cookie identifies a HTTP cookie. When used within a Response the Cookie DSL
+// also makes it possible to define the cookie attributes.
+//
+// Cookie must appear in the API HTTP expression (to define request cookies
+// common to all the API endpoints), a service HTTP expression (to define
+// request cookies common to all the service endpoints) a specific method HTTP
+// expression (to define request cookies) or a Response expression (to define
+// the response cookies).
+//
+// Cookie accepts the same arguments as the Attribute function. The cookie name
+// may define a mapping between the attribute name and the cookie name. The
+// mapping syntax is "name of attribute:name of cookie".
+//
+// Example:
+//
+//    var _ = Service("account", func() {
+//        Method("create", func() {
+//            Payload(func() {
+//                Attribute("session", String, "ID of current session")
+//            })
+//            Result(Account)
+//            HTTP(func() {
+//                // Initialize payload's "session" attribute with the value of
+//                // the SID cookie after validating that's it's a valid GUID.
+//                Cookie("session:SID", String, func() {
+//                    Format(FormatGUID)
+//                })
+//                Response(StatusCreated, func() {
+//                    // Write the value of the result "session" attribute to
+//                    // the cookie named "SID" and initialize the cookie
+//                    // "max-age", "domain", "path", "secure" and "http-only"
+//                    // attributes. When reading the cookie value client
+//                    // side validate that's it is a GUID.
+//                    Cookie("session:SID", String, func() {
+//                        CookieMaxAge(3600)      // Cookie attributes
+//                        CookieDomain("goa.design")
+//                        CookiePath("/session")
+//                        CookieSecure()
+//                        CookieHTTPOnly()
+//
+//                        Format(FormatGUID)      // Cookie value validations
+//                    })
+//                })
+//            })
+//        })
+//    })
+//
+func Cookie(name string, args ...interface{}) {
+	h := cookies(eval.Current())
+	if h == nil {
+		eval.IncompatibleDSL()
+		return
+	}
+	if name == "" {
+		eval.ReportError("header name cannot be empty")
+	}
+	eval.Execute(func() { Attribute(name, args...) }, h.AttributeExpr)
+	h.Remap()
+}
+
+// CookieMaxAge defines the "max-age" attribute of a HTTP response cookie.
+//
+// CookieMaxAge must appear in a Cookie expression.
+//
+// CookieMaxAge accepts one argument which is the max-age value.
+//
+// Example:
+//
+//    var _ = Service("account", func() {
+//        Method("create", func() {
+//            Result(Account)
+//            HTTP(func() {
+//                Response(StatusCreated, func() {
+//                    Cookie("session:SID", String, func() {
+//                        CookieMaxAge(3600)
+//                    })
+//                })
+//            })
+//        })
+//    })
+//
+func CookieMaxAge(n int) {
+	_, ok := eval.Current().(*expr.HTTPResponseExpr)
+	if !ok {
+		eval.IncompatibleDSL()
+		return
+	}
+	cookieAttribute("max-age", strconv.Itoa(n))
+}
+
+// CookieDomain defines the "domain" attribute of a HTTP response cookie.
+//
+// CookieDomain must appear in a Cookie expression.
+//
+// CookieDomain accepts one argument which is the path value.
+//
+// Example:
+//
+//    var _ = Service("account", func() {
+//        Method("create", func() {
+//            Result(Account)
+//            HTTP(func() {
+//                Response(StatusCreated, func() {
+//                    Cookie("session:SID", String, func() {
+//                        CookieDomain("goa.design")
+//                    })
+//                })
+//            })
+//        })
+//    })
+//
+func CookieDomain(d string) {
+	_, ok := eval.Current().(*expr.HTTPResponseExpr)
+	if !ok {
+		eval.IncompatibleDSL()
+		return
+	}
+	cookieAttribute("domain", d)
+}
+
+// CookiePath defines the "path" attribute of a HTTP response cookie.
+//
+// CookiePath must appear in a Cookie expression.
+//
+// CookiePath accepts one argument which is the path value.
+//
+// Example:
+//
+//    var _ = Service("account", func() {
+//        Method("create", func() {
+//            Result(Account)
+//            HTTP(func() {
+//                Response(StatusCreated, func() {
+//                    Cookie("session:SID", String, func() {
+//                        CookiePath("/session")
+//                    })
+//                })
+//            })
+//        })
+//    })
+//
+func CookiePath(p string) {
+	_, ok := eval.Current().(*expr.HTTPResponseExpr)
+	if !ok {
+		eval.IncompatibleDSL()
+		return
+	}
+	cookieAttribute("path", p)
+}
+
+// CookieSecure initializes the "secute" attribute of a HTTP response cookie
+// with "Secure".
+//
+// CookieSecure must appear in a Cookie expression.
+//
+// Example:
+//
+//    var _ = Service("account", func() {
+//        Method("create", func() {
+//            Result(Account)
+//            HTTP(func() {
+//                Response(StatusCreated, func() {
+//                    Cookie("session:SID", String, func() {
+//                        CookieSecure()
+//                    })
+//                })
+//            })
+//        })
+//    })
+//
+func CookieSecure() {
+	_, ok := eval.Current().(*expr.HTTPResponseExpr)
+	if !ok {
+		eval.IncompatibleDSL()
+		return
+	}
+	cookieAttribute("secure", "Secure")
+}
+
+// CookieHTTPOnly initializes the "http-only" attribute of a HTTP response
+// cookie with "HttpOnly".
+//
+// CookieHTTPOnly must appear in a Cookie expression.
+//
+// Example:
+//
+//    var _ = Service("account", func() {
+//        Method("create", func() {
+//            Result(Account)
+//            HTTP(func() {
+//                Response(StatusCreated, func() {
+//                    Cookie("session:SID", String, func() {
+//                        CookieHTTPOnly()
+//                    })
+//                })
+//            })
+//        })
+//    })
+//
+func CookieHTTPOnly() {
+	_, ok := eval.Current().(*expr.MappedAttributeExpr)
+	if !ok {
+		eval.IncompatibleDSL()
+		return
+	}
+	cookieAttribute("http-only", "HttpOnly")
+}
+
 // Params groups a set of Param expressions. It makes it possible to list
 // required parameters using the Required function.
 //
@@ -911,6 +1121,37 @@ func headers(exp eval.Expression) *expr.MappedAttributeExpr {
 	}
 }
 
+// cookies returns the mapped attribute containing the cookies for the given
+// expression if it's either the root, a service or an endpoint - nil otherwise.
+func cookies(exp eval.Expression) *expr.MappedAttributeExpr {
+	switch e := exp.(type) {
+	case *expr.RootExpr:
+		if e.API.HTTP.Cookies == nil {
+			e.API.HTTP.Cookies = expr.NewEmptyMappedAttributeExpr()
+		}
+		return e.API.HTTP.Cookies
+	case *expr.HTTPServiceExpr:
+		if e.Cookies == nil {
+			e.Cookies = expr.NewEmptyMappedAttributeExpr()
+		}
+		return e.Cookies
+	case *expr.HTTPEndpointExpr:
+		if e.Cookies == nil {
+			e.Cookies = expr.NewEmptyMappedAttributeExpr()
+		}
+		return e.Cookies
+	case *expr.HTTPResponseExpr:
+		if e.Cookies == nil {
+			e.Cookies = expr.NewEmptyMappedAttributeExpr()
+		}
+		return e.Cookies
+	case *expr.MappedAttributeExpr:
+		return e
+	default:
+		return nil
+	}
+}
+
 // params returns the mapped attribute containing the path and query params for
 // the given expression if it's either the root, a API server, a service or an
 // endpoint - nil otherwise.
@@ -937,3 +1178,13 @@ func params(exp eval.Expression) *expr.MappedAttributeExpr {
 		return nil
 	}
 }
+
+// cookieAttribute initialize the current attribute metadata with the details of
+// a HTTP cookie attribute for use by the HTTP code generator.
+func cookieAttribute(name, value string) {
+	c := eval.Current().(*expr.HTTPResponseExpr).Cookies
+	if c.Meta == nil {
+		c.Meta = expr.MetaExpr{}
+	}
+	c.Meta["cookie:"+name] = []string{value}
+}
diff --git a/dsl/security.go b/dsl/security.go
index dd33ba94..43d7ed52 100644
--- a/dsl/security.go
+++ b/dsl/security.go
@@ -291,9 +291,7 @@ func Security(args ...interface{}) {
 // NoSecurity must appear in Method.
 func NoSecurity() {
 	security := &expr.SecurityExpr{
-		Schemes: []*expr.SchemeExpr{
-			&expr.SchemeExpr{Kind: expr.NoKind},
-		},
+		Schemes: []*expr.SchemeExpr{{Kind: expr.NoKind}},
 	}
 
 	current := eval.Current()
diff --git a/expr/attribute.go b/expr/attribute.go
index 755649cd..b18dfad9 100644
--- a/expr/attribute.go
+++ b/expr/attribute.go
@@ -507,18 +507,16 @@ func (a *AttributeExpr) Delete(name string) {
 // Debug dumps the attribute to STDOUT in a goa developer friendly way.
 func (a *AttributeExpr) Debug(prefix string) { a.debug(prefix, make(map[*AttributeExpr]int), 0) }
 func (a *AttributeExpr) debug(prefix string, seen map[*AttributeExpr]int, indent int) {
-	for i := 0; i < indent; i++ {
-		prefix = "  " + prefix
-	}
+	prefix = strings.Repeat("  ", indent) + prefix
 	if c, ok := seen[a]; ok && c > 1 {
 		fmt.Printf("%s: ...\n", prefix)
 		return
 	}
 	seen[a]++
-	fmt.Printf("%s: %q\n", prefix, a.Type.Name())
+	fmt.Printf("%s: %s\n", prefix, a.Type.Name())
 	if o := AsObject(a.Type); o != nil {
 		for _, att := range *o {
-			att.Attribute.debug(" - "+att.Name, seen, indent+1)
+			att.Attribute.debug("- "+att.Name, seen, indent+1)
 		}
 	}
 	if a := AsArray(a.Type); a != nil {
@@ -528,6 +526,13 @@ func (a *AttributeExpr) debug(prefix string, seen map[*AttributeExpr]int, indent
 		m.KeyType.debug(" Key", seen, indent+2)
 		m.ElemType.debug(" Elem", seen, indent+2)
 	}
+	if d := a.DefaultValue; d != nil {
+		fmt.Printf("%s: default value\n", prefix)
+		fmt.Printf("%s%#v", strings.Repeat("  ", indent+1), a.DefaultValue)
+	}
+	if v := a.Validation; v != nil {
+		v.Debug(indent + 1)
+	}
 }
 
 // validateEnumDefault makes sure that the attribute default value is one of the
@@ -697,6 +702,36 @@ func (v *ValidationExpr) Dup() *ValidationExpr {
 	}
 }
 
+// Debug dumps the validation to STDOUT in a goa developer friendly way.
+func (v *ValidationExpr) Debug(indent int) {
+	prefix := strings.Repeat("  ", indent)
+	fmt.Printf("%svalidations\n", prefix)
+	if len(v.Values) > 0 {
+		fmt.Printf("%s- enum: %s\n", prefix, fmt.Sprintf("%v", v.Values))
+	}
+	if v.Format != "" {
+		fmt.Printf("%s- format: %s\n", prefix, v.Format)
+	}
+	if v.Pattern != "" {
+		fmt.Printf("%s- pattern: %s\n", prefix, v.Pattern)
+	}
+	if v.Minimum != nil {
+		fmt.Printf("%s- min: %v\n", prefix, *v.Minimum)
+	}
+	if v.Maximum != nil {
+		fmt.Printf("%s- max: %v\n", prefix, *v.Maximum)
+	}
+	if v.MinLength != nil {
+		fmt.Printf("%s- minLength: %v\n", prefix, *v.MinLength)
+	}
+	if v.MaxLength != nil {
+		fmt.Printf("%s- minLength: %v\n", prefix, *v.MaxLength)
+	}
+	if len(v.Required) > 0 {
+		fmt.Printf("%s- required: %v\n", prefix, v.Required)
+	}
+}
+
 // IsSupportedValidationFormat checks if the validation format is supported by goa.
 func (a *AttributeExpr) IsSupportedValidationFormat(vf ValidationFormat) bool {
 	switch vf {
diff --git a/expr/http.go b/expr/http.go
index 5534bbac..23a41899 100644
--- a/expr/http.go
+++ b/expr/http.go
@@ -16,6 +16,9 @@ type (
 		// Headers defines the HTTP request headers common to to all
 		// the API endpoints.
 		Headers *MappedAttributeExpr
+		// Cookies defines the HTTP request cookies common to to all
+		// the API endpoints.
+		Cookies *MappedAttributeExpr
 		// Consumes lists the mime types supported by the API
 		// controllers.
 		Consumes []string
diff --git a/expr/http_body_types.go b/expr/http_body_types.go
index d95941eb..71d90181 100644
--- a/expr/http_body_types.go
+++ b/expr/http_body_types.go
@@ -73,13 +73,14 @@ func httpRequestBody(a *HTTPEndpointExpr) *AttributeExpr {
 	var (
 		payload  = a.MethodExpr.Payload
 		headers  = a.Headers
+		cookies  = a.Cookies
 		params   = a.Params
-		bodyOnly = headers.IsEmpty() && params.IsEmpty() && a.MapQueryParams == nil
+		bodyOnly = headers.IsEmpty() && params.IsEmpty() && cookies.IsEmpty() && a.MapQueryParams == nil
 	)
 
-	// 1. If Payload is not an object then check whether there are params or
-	// headers defined and if so return empty type (payload encoded in
-	// request params or headers) otherwise return payload type (payload
+	// 1. If Payload is not an object then check whether there are params,
+	// cookies or headers defined and if so return empty type (payload encoded
+	// in request params or headers) otherwise return payload type (payload
 	// encoded in request body).
 	if !IsObject(payload.Type) {
 		if bodyOnly {
@@ -93,6 +94,7 @@ func httpRequestBody(a *HTTPEndpointExpr) *AttributeExpr {
 	// 2. Remove header and param attributes
 	body := NewMappedAttributeExpr(payload)
 	removeAttributes(body, headers)
+	removeAttributes(body, cookies)
 	removeAttributes(body, params)
 	if a.MapQueryParams != nil && *a.MapQueryParams != "" {
 		removeAttribute(body, *a.MapQueryParams)
@@ -151,7 +153,7 @@ func httpStreamingBody(e *HTTPEndpointExpr) *AttributeExpr {
 // the given endpoint and response. If the DSL defines a body explicitly via the
 // Body function then the corresponding attribute is used. Otherwise the
 // attribute is computed by removing the attributes of the method payload used
-// to define headers.
+// to define cookies and headers.
 func httpResponseBody(a *HTTPEndpointExpr, resp *HTTPResponseExpr) *AttributeExpr {
 	var name, suffix string
 	if len(a.Responses) > 1 {
@@ -164,7 +166,7 @@ func httpResponseBody(a *HTTPEndpointExpr, resp *HTTPResponseExpr) *AttributeExp
 // httpErrorResponseBody returns an attribute describing the response body of a
 // given error. If the DSL defines a body explicitly via the Body function then
 // the corresponding attribute is returned. Otherwise the attribute is computed
-// by removing the attributes of the error used to define headers and
+// by removing the attributes of the error used to define cookies, headers and
 // parameters.
 func httpErrorResponseBody(e *HTTPEndpointExpr, v *HTTPErrorExpr) *AttributeExpr {
 	name := e.Name() + "_" + v.ErrorExpr.Name
@@ -194,12 +196,12 @@ func buildHTTPResponseBody(name string, attr *AttributeExpr, resp *HTTPResponseE
 		return att
 	}
 
-	// 1. If attribute is not an object then check whether there are headers
-	// defined and if so return empty type (attr encoded in response
-	// headers) otherwise return renamed attr type (attr encoded in
+	// 1. If attribute is not an object then check whether there are headers or
+	// cookies defined and if so return empty type (attr encoded in response
+	// header or cookie) otherwise return renamed attr type (attr encoded in
 	// response body).
 	if !IsObject(attr.Type) {
-		if resp.Headers.IsEmpty() {
+		if resp.Headers.IsEmpty() && resp.Cookies.IsEmpty() {
 			attr = DupAtt(attr)
 			renameType(attr, name, "Response") // Do not use ResponseBody as it could clash with name of element
 			return attr
@@ -208,8 +210,9 @@ func buildHTTPResponseBody(name string, attr *AttributeExpr, resp *HTTPResponseE
 	}
 	body := NewMappedAttributeExpr(attr)
 
-	// 2. Remove header attributes
+	// 2. Remove header and cookie attributes
 	removeAttributes(body, resp.Headers)
+	removeAttributes(body, resp.Cookies)
 
 	// 3. Return empty type if no attribute left
 	if len(*AsObject(body.Type)) == 0 {
@@ -235,6 +238,7 @@ func buildHTTPResponseBody(name string, attr *AttributeExpr, resp *HTTPResponseE
 	for i, v := range rt.Views {
 		mv := NewMappedAttributeExpr(v.AttributeExpr)
 		removeAttributes(mv, resp.Headers)
+		removeAttributes(mv, resp.Cookies)
 		nv := &ViewExpr{
 			AttributeExpr: mv.Attribute(),
 			Name:          v.Name,
diff --git a/expr/http_endpoint.go b/expr/http_endpoint.go
index f15cbf6b..c0cdec4f 100644
--- a/expr/http_endpoint.go
+++ b/expr/http_endpoint.go
@@ -38,6 +38,8 @@ type (
 		Params *MappedAttributeExpr
 		// Headers defines the HTTP request headers.
 		Headers *MappedAttributeExpr
+		// Cookies defines the HTTP request cookies.
+		Cookies *MappedAttributeExpr
 		// Body describes the HTTP request body.
 		Body *AttributeExpr
 		// StreamingBody describes the body transferred through the websocket
@@ -182,15 +184,22 @@ func (e *HTTPEndpointExpr) Prepare() {
 	if e.Headers == nil {
 		e.Headers = NewEmptyMappedAttributeExpr()
 	}
+	if e.Cookies == nil {
+		e.Cookies = NewEmptyMappedAttributeExpr()
+	}
 	if e.Params == nil {
 		e.Params = NewEmptyMappedAttributeExpr()
 	}
 
-	// Inherit headers and params from parent service and API
+	// Inherit headers, cookies and params from parent service and API
 	headers := NewEmptyMappedAttributeExpr()
 	headers.Merge(Root.API.HTTP.Headers)
 	headers.Merge(e.Service.Headers)
 
+	cookies := NewEmptyMappedAttributeExpr()
+	cookies.Merge(Root.API.HTTP.Cookies)
+	cookies.Merge(e.Service.Cookies)
+
 	params := NewEmptyMappedAttributeExpr()
 	params.Merge(Root.API.HTTP.Params)
 	params.Merge(e.Service.Params)
@@ -641,6 +650,7 @@ func (e *HTTPEndpointExpr) Finalize() {
 	// payload attributes.
 	initAttr(e.Params, e.MethodExpr.Payload)
 	initAttr(e.Headers, e.MethodExpr.Payload)
+	initAttr(e.Cookies, e.MethodExpr.Payload)
 
 	if e.Body != nil {
 		// rename type to add RequestBody suffix so that we don't end with
diff --git a/expr/http_response.go b/expr/http_response.go
index 654d0907..f0ac8180 100644
--- a/expr/http_response.go
+++ b/expr/http_response.go
@@ -84,6 +84,8 @@ type (
 		Description string
 		// Headers describe the HTTP response headers.
 		Headers *MappedAttributeExpr
+		// Cookies describe the HTTP response cookies.
+		Cookies *MappedAttributeExpr
 		// Response body if any
 		Body *AttributeExpr
 		// Response Content-Type header value
@@ -114,6 +116,9 @@ func (r *HTTPResponseExpr) Prepare() {
 	if r.Headers == nil {
 		r.Headers = NewEmptyMappedAttributeExpr()
 	}
+	if r.Cookies == nil {
+		r.Cookies = NewEmptyMappedAttributeExpr()
+	}
 }
 
 // Validate checks that the response definition is consistent: its status is set
@@ -193,13 +198,34 @@ func (r *HTTPResponseExpr) Validate(e *HTTPEndpointExpr) *eval.ValidationErrors
 				}
 			}
 		} else if len(*AsObject(r.Headers.Type)) > 1 {
-			verr.Add(r, "response defines more than one header but result type is not an object")
+			verr.Add(r, "response defines more than one headers but result type is not an object")
 		} else if IsArray(e.MethodExpr.Result.Type) {
 			if !IsPrimitive(AsArray(e.MethodExpr.Result.Type).ElemType.Type) {
 				verr.Add(e, "Array result is mapped to an HTTP header but is not an array of primitive types.")
 			}
 		}
 	}
+	if !r.Cookies.IsEmpty() {
+		verr.Merge(r.Cookies.Validate("HTTP response cookies", r))
+		if e.MethodExpr.Result.Type == Empty {
+			verr.Add(r, "response defines cookies but result is empty")
+		} else if IsObject(e.MethodExpr.Result.Type) {
+			mobj := AsObject(r.Cookies.Type)
+			for _, c := range *mobj {
+				t := resultAttributeType(c.Name)
+				if t == nil {
+					verr.Add(r, "cookie %q has no equivalent attribute in%s result type, use notation 'attribute_name:cookie_name' to identify corresponding result type attribute.", c.Name, inview)
+				}
+				if !IsPrimitive(t) {
+					verr.Add(e, "attribute %q used in HTTP cookies must be a primitive type.", c.Name)
+				}
+			}
+		} else if len(*AsObject(r.Cookies.Type)) > 1 {
+			verr.Add(r, "response defines more than one cookies but result type is not an object")
+		} else if IsArray(e.MethodExpr.Result.Type) {
+			verr.Add(e, "Array result is mapped to an HTTP cookie.")
+		}
+	}
 	if r.Body != nil {
 		verr.Merge(r.Body.Validate("HTTP response body", r))
 		if e.SkipResponseBodyEncodeDecode {
@@ -219,7 +245,7 @@ func (r *HTTPResponseExpr) Validate(e *HTTPEndpointExpr) *eval.ValidationErrors
 	} else if e.SkipResponseBodyEncodeDecode {
 		body := httpResponseBody(e, r)
 		if body.Type != Empty {
-			verr.Add(e, "HTTP endpoint response body must be empty when using SkipResponseBodyEncodeDecode. Make sure to define Headers as needed.")
+			verr.Add(e, "HTTP endpoint response body must be empty when using SkipResponseBodyEncodeDecode. Make sure to define headers and cookies as needed.")
 		}
 	}
 	return verr
@@ -272,6 +298,7 @@ func (r *HTTPResponseExpr) Finalize(a *HTTPEndpointExpr, svcAtt *AttributeExpr)
 		}
 	}
 	initAttr(r.Headers, svcAtt)
+	initAttr(r.Cookies, svcAtt)
 }
 
 // Dup creates a copy of the response expression.
@@ -289,6 +316,9 @@ func (r *HTTPResponseExpr) Dup() *HTTPResponseExpr {
 	if r.Headers != nil {
 		res.Headers = DupMappedAtt(r.Headers)
 	}
+	if r.Cookies != nil {
+		res.Cookies = DupMappedAtt(r.Cookies)
+	}
 	return &res
 }
 
diff --git a/expr/http_service.go b/expr/http_service.go
index eed7c84c..7a959e55 100644
--- a/expr/http_service.go
+++ b/expr/http_service.go
@@ -27,6 +27,9 @@ type (
 		// Headers defines the HTTP request headers common to all the
 		// service endpoints.
 		Headers *MappedAttributeExpr
+		// Cookies defines the HTTP request cookies common to all the
+		// service endpoints.
+		Cookies *MappedAttributeExpr
 		// Name of parent service if any
 		ParentName string
 		// Endpoint with canonical service path
diff --git a/http/codegen/client.go b/http/codegen/client.go
index 65b191cd..470d5f57 100644
--- a/http/codegen/client.go
+++ b/http/codegen/client.go
@@ -367,7 +367,7 @@ func (c *{{ .ClientStruct }}) {{ .EndpointInit }}({{ if .MultipartRequestEncoder
 
 // input: EndpointData
 const requestBuilderT = `{{ comment .RequestInit.Description }}
-func (c *{{ .ClientStruct }}) {{ .RequestInit.Name }}(ctx context.Context, {{ range .RequestInit.ClientArgs }}{{ .Name }} {{ .TypeRef }},{{ end }}) (*http.Request, error) {
+func (c *{{ .ClientStruct }}) {{ .RequestInit.Name }}(ctx context.Context, {{ range .RequestInit.ClientArgs }}{{ .VarName }} {{ .TypeRef }},{{ end }}) (*http.Request, error) {
 	{{- .RequestInit.ClientCode }}
 }
 `
@@ -422,6 +422,39 @@ func {{ .RequestEncoder }}(encoder func(*http.Request) goahttp.Encoder) func(*ht
 		}
 		{{- end }}
 	{{- end }}
+	{{- range .Payload.Request.Cookies }}
+		{{- if .FieldName }}
+			{{- if .FieldPointer }}
+		if p.{{ .FieldName }} != nil {
+			{{- else }}
+			{
+			{{- end }}
+			v{{ if not (eq .Type.Name "string") }}raw{{ end }} := {{ if .FieldPointer }}*{{ end }}p.{{ .FieldName }}
+			{{- if not (eq .Type.Name "string" ) }}
+			{{ template "type_conversion" (typeConversionData .Type .FieldType "vraw" "v") }}
+			{{- end }}
+			req.AddCookie(&http.Cookie{
+				Name: {{ printf "%q" .Name }},
+				Value: v,
+				{{- if .MaxAge }}
+				MaxAge: {{ .MaxAge }},
+				{{- end }}
+				{{- if .Path }}
+				Path: {{ .Path }},
+				{{- end }}
+				{{- if .Domain }}
+				Domain: {{ .Domain }},
+				{{- end }}
+				{{- if .Secure }}
+				Secure: true,
+				{{- end }}
+				{{- if .HTTPOnly }}
+				HttpOnly: true,
+				{{- end }}
+			})
+		}
+		{{- end }}
+	{{- end }}
 	{{- if or .Payload.Request.QueryParams }}
 		values := req.URL.Query()
 	{{- end }}
@@ -482,7 +515,7 @@ func {{ .RequestEncoder }}(encoder func(*http.Request) goahttp.Encoder) func(*ht
 		}
 	{{- else if .Payload.Request.ClientBody }}
 		{{- if .Payload.Request.ClientBody.Init }}
-		body := {{ .Payload.Request.ClientBody.Init.Name }}({{ range .Payload.Request.ClientBody.Init.ClientArgs }}{{ if .FieldPointer }}&{{ end }}{{ .Name }}, {{ end }})
+		body := {{ .Payload.Request.ClientBody.Init.Name }}({{ range .Payload.Request.ClientBody.Init.ClientArgs }}{{ if .FieldPointer }}&{{ end }}{{ .VarName }}, {{ end }})
 		{{- else }}
 		body := p{{ if .Payload.Request.PayloadAttr }}.{{ .Payload.Request.PayloadAttr }}{{ end }}
 		{{- end }}
@@ -632,6 +665,8 @@ func {{ .ResponseDecoder }}(decoder func(*http.Response) goahttp.Decoder, restor
 			return body, nil
 		{{- else if .Headers }}
 			return {{ (index .Headers 0).VarName }}, nil
+		{{- else if .Cookies }}
+			return {{ (index .Cookies 0).VarName }}, nil
 		{{- else }}
 			return nil, nil
 		{{- end }}
@@ -790,6 +825,74 @@ const singleResponseT = ` {{- if .ClientBody }}
 		{{- end }}{{/* range .Headers */}}
 	{{- end }}
 
+	{{- if .Cookies }}
+			var (
+		{{- range .Cookies }}
+				{{ .VarName }}    {{ .TypeRef }}
+				{{ .VarName }}Raw string
+		{{- end }}
+
+				cookies = resp.Cookies()
+		{{- if not .ClientBody }}
+			{{- if .MustValidate }}
+				err error
+			{{- end }}
+		{{- end }}
+			)
+        for _, c := range cookies {
+			switch c.Name {
+		{{- range .Cookies }}
+			case {{ printf "%q" .Name }}:
+				{{ .VarName }}Raw = c.Value
+		{{- end }}
+			}
+		}
+		{{- range .Cookies }}
+
+		{{- if (or (eq .Type.Name "string") (eq .Type.Name "any")) }}
+			{{- if .Required }}
+				if {{ .VarName }}Raw == "" {
+					err = goa.MergeErrors(err, goa.MissingFieldError("{{ .Name }}", "cookie"))
+				}
+				{{ .VarName }} = {{ if and (eq .Type.Name "string") .Pointer }}&{{ end }}{{ .VarName }}Raw
+			{{- else }}
+				if {{ .VarName }}Raw != "" {
+					{{ .VarName }} = {{ if and (eq .Type.Name "string") .Pointer }}&{{ end }}{{ .VarName }}Raw
+				}
+				{{- if .DefaultValue }} else {
+					{{ .VarName }} = {{ if eq .Type.Name "string" }}{{ printf "%q" .DefaultValue }}{{ else }}{{ printf "%#v" .DefaultValue }}{{ end }}
+				}
+				{{- end }}
+			{{- end }}
+
+		{{- else }}{{/* not string and not any */}}
+		{
+			{{- if .Required }}
+			if {{ .VarName }}Raw == "" {
+				return nil, goahttp.ErrValidationError("{{ $.ServiceName }}", "{{ $.Method.Name }}", goa.MissingFieldError("{{ .Name }}", "cookie"))
+			}
+			{{- else if .DefaultValue }}
+			if {{ .VarName }}Raw == "" {
+				{{ .VarName }} = {{ printf "%#v" .DefaultValue }}
+			}
+			{{- end }}
+
+			{{- if .DefaultValue }}else {
+				{{- else if not .Required }}
+			if {{ .VarName }}Raw != "" {
+			{{- end }}
+				{{- template "type_conversion" . }}
+			{{- if or .DefaultValue (not .Required) }}
+			}
+			{{- end }}
+		}
+		{{- end }}
+		{{- if .Validate }}
+			{{ .Validate }}
+		{{- end }}
+		{{- end }}{{/* range .Cookies */}}
+	{{- end }}
+
 	{{- if .MustValidate }}
 			if err != nil {
 				return nil, goahttp.ErrValidationError("{{ $.ServiceName }}", "{{ $.Method.Name }}", err)
diff --git a/http/codegen/client_cli.go b/http/codegen/client_cli.go
index 6f95622c..4a2f9d45 100644
--- a/http/codegen/client_cli.go
+++ b/http/codegen/client_cli.go
@@ -212,7 +212,7 @@ func makeFlags(e *EndpointData, args []*InitArgData, payload expr.DataType) ([]*
 	)
 	for i, arg := range args {
 		pInitArgs[i] = &codegen.InitArgData{
-			Name:         arg.Name,
+			Name:         arg.VarName,
 			Pointer:      arg.Pointer,
 			FieldName:    arg.FieldName,
 			FieldPointer: arg.FieldPointer,
@@ -220,13 +220,13 @@ func makeFlags(e *EndpointData, args []*InitArgData, payload expr.DataType) ([]*
 			Type:         arg.Type,
 		}
 
-		f := cli.NewFlagData(e.ServiceName, e.Method.Name, arg.Name, arg.TypeName, arg.Description, arg.Required, arg.Example, arg.DefaultValue)
+		f := cli.NewFlagData(e.ServiceName, e.Method.Name, arg.VarName, arg.TypeName, arg.Description, arg.Required, arg.Example, arg.DefaultValue)
 		flags[i] = f
 		params[i] = f.FullName
-		if arg.FieldName == "" && arg.Name != "body" {
+		if arg.FieldName == "" && arg.VarName != "body" {
 			continue
 		}
-		code, chek := cli.FieldLoadCode(f, arg.Name, arg.TypeName, arg.Validate, arg.DefaultValue, payload)
+		code, chek := cli.FieldLoadCode(f, arg.VarName, arg.TypeName, arg.Validate, arg.DefaultValue, payload)
 		check = check || chek
 		tn := arg.TypeRef
 		if f.Type == "JSON" {
@@ -236,8 +236,8 @@ func makeFlags(e *EndpointData, args []*InitArgData, payload expr.DataType) ([]*
 			tn = arg.TypeName
 		}
 		fdata = append(fdata, &cli.FieldData{
-			Name:    arg.Name,
-			VarName: arg.Name,
+			Name:    arg.VarName,
+			VarName: arg.VarName,
 			TypeRef: tn,
 			Init:    code,
 		})
diff --git a/http/codegen/client_types.go b/http/codegen/client_types.go
index bd4c986b..498b04d0 100644
--- a/http/codegen/client_types.go
+++ b/http/codegen/client_types.go
@@ -222,7 +222,7 @@ func clientType(genpkg string, svc *expr.HTTPServiceExpr, seen map[string]struct
 
 // input: InitData
 const clientBodyInitT = `{{ comment .Description }}
-func {{ .Name }}({{ range .ClientArgs }}{{ .Name }} {{.TypeRef }}, {{ end }}) {{ .ReturnTypeRef }} {
+func {{ .Name }}({{ range .ClientArgs }}{{ .VarName }} {{.TypeRef }}, {{ end }}) {{ .ReturnTypeRef }} {
 	{{ .ClientCode }}
 	return body
 }
@@ -230,7 +230,7 @@ func {{ .Name }}({{ range .ClientArgs }}{{ .Name }} {{.TypeRef }}, {{ end }}) {{
 
 // input: InitData
 const clientTypeInitT = `{{ comment .Description }}
-func {{ .Name }}({{- range .ClientArgs }}{{ .Name }} {{ .TypeRef }}, {{ end }}) {{ .ReturnTypeRef }} {
+func {{ .Name }}({{- range .ClientArgs }}{{ .VarName }} {{ .TypeRef }}, {{ end }}) {{ .ReturnTypeRef }} {
 {{- if .ClientCode }}
 	{{ .ClientCode }}
 	{{- if .ReturnTypeAttribute }}
diff --git a/http/codegen/example_cli.go b/http/codegen/example_cli.go
index f6943413..644c7046 100644
--- a/http/codegen/example_cli.go
+++ b/http/codegen/example_cli.go
@@ -70,8 +70,8 @@ func exampleCLI(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *codeg
 	}
 	sections := []*codegen.SectionTemplate{
 		codegen.Header("", "main", specs),
-		&codegen.SectionTemplate{Name: "cli-http-start", Source: httpCLIStartT},
-		&codegen.SectionTemplate{
+		{Name: "cli-http-start", Source: httpCLIStartT},
+		{
 			Name:   "cli-http-streaming",
 			Source: httpCLIStreamingT,
 			Data: map[string]interface{}{
@@ -81,7 +81,7 @@ func exampleCLI(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *codeg
 				"needStream": needStream,
 			},
 		},
-		&codegen.SectionTemplate{
+		{
 			Name:   "cli-http-end",
 			Source: httpCLIEndT,
 			Data: map[string]interface{}{
@@ -93,7 +93,7 @@ func exampleCLI(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *codeg
 				"hasWebSocket": hasWebSocket,
 			},
 		},
-		&codegen.SectionTemplate{Name: "cli-http-usage", Source: httpCLIUsageT},
+		{Name: "cli-http-usage", Source: httpCLIUsageT},
 	}
 	return &codegen.File{
 		Path:             path,
diff --git a/http/codegen/example_server.go b/http/codegen/example_server.go
index 5085091a..87c8c437 100644
--- a/http/codegen/example_server.go
+++ b/http/codegen/example_server.go
@@ -83,17 +83,17 @@ func exampleServer(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *co
 
 	sections := []*codegen.SectionTemplate{
 		codegen.Header("", "main", specs),
-		&codegen.SectionTemplate{
+		{
 			Name:   "server-http-start",
 			Source: httpSvrStartT,
 			Data: map[string]interface{}{
 				"Services": svcdata,
 			},
 		},
-		&codegen.SectionTemplate{Name: "server-http-logger", Source: httpSvrLoggerT},
-		&codegen.SectionTemplate{Name: "server-http-encoding", Source: httpSvrEncodingT},
-		&codegen.SectionTemplate{Name: "server-http-mux", Source: httpSvrMuxT},
-		&codegen.SectionTemplate{
+		{Name: "server-http-logger", Source: httpSvrLoggerT},
+		{Name: "server-http-encoding", Source: httpSvrEncodingT},
+		{Name: "server-http-mux", Source: httpSvrMuxT},
+		{
 			Name:   "server-http-init",
 			Source: httpSvrInitT,
 			Data: map[string]interface{}{
@@ -102,15 +102,15 @@ func exampleServer(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *co
 			},
 			FuncMap: map[string]interface{}{"needStream": needStream, "hasWebSocket": hasWebSocket},
 		},
-		&codegen.SectionTemplate{Name: "server-http-middleware", Source: httpSvrMiddlewareT},
-		&codegen.SectionTemplate{
+		{Name: "server-http-middleware", Source: httpSvrMiddlewareT},
+		{
 			Name:   "server-http-end",
 			Source: httpSvrEndT,
 			Data: map[string]interface{}{
 				"Services": svcdata,
 			},
 		},
-		&codegen.SectionTemplate{Name: "server-http-errorhandler", Source: httpSvrErrorHandlerT},
+		{Name: "server-http-errorhandler", Source: httpSvrErrorHandlerT},
 	}
 
 	return &codegen.File{Path: fpath, SectionTemplates: sections, SkipExist: true}
diff --git a/http/codegen/paths.go b/http/codegen/paths.go
index c8ee25b9..ade7c7e8 100644
--- a/http/codegen/paths.go
+++ b/http/codegen/paths.go
@@ -60,7 +60,7 @@ func pathSections(svc *expr.HTTPServiceExpr, pkg string) []*codegen.SectionTempl
 
 // input: EndpointData
 const pathT = `{{ range .Routes }}// {{ .PathInit.Description }}
-func {{ .PathInit.Name }}({{ range .PathInit.ServerArgs }}{{ .Name }} {{ .TypeRef }}, {{ end }}) {{ .PathInit.ReturnTypeRef }} {
+func {{ .PathInit.Name }}({{ range .PathInit.ServerArgs }}{{ .VarName }} {{ .TypeRef }}, {{ end }}) {{ .PathInit.ReturnTypeRef }} {
 {{- .PathInit.ServerCode }}
 }
 {{ end }}`
diff --git a/http/codegen/server.go b/http/codegen/server.go
index b3b74226..995dbcbf 100644
--- a/http/codegen/server.go
+++ b/http/codegen/server.go
@@ -525,7 +525,7 @@ func {{ .RequestDecoder }}(mux goahttp.Muxer, decoder func(*http.Request) goahtt
 		{{- end }}
 {{- end }}
 {{- if not .MultipartRequestDecoder }}
-	{{- template "request_params_headers" .Payload.Request }}
+	{{- template "request_elements" .Payload.Request }}
 	{{- if .Payload.Request.MustValidate }}
 		if err != nil {
 			return nil, err
@@ -566,11 +566,11 @@ func {{ .RequestDecoder }}(mux goahttp.Muxer, decoder func(*http.Request) goahtt
 	return payload, nil
 	}
 }
-` + requestParamsHeadersT
+` + requestElementsT
 
 // input: RequestData
-const requestParamsHeadersT = `{{- define "request_params_headers" }}
-{{- if or .PathParams .QueryParams .Headers }}
+const requestElementsT = `{{- define "request_elements" }}
+{{- if or .PathParams .QueryParams .Headers .Cookies }}
 {{- if .ServerBody }}{{/* we want a newline only if there was code before */}}
 {{ end }}
 		var (
@@ -583,9 +583,15 @@ const requestParamsHeadersT = `{{- define "request_params_headers" }}
 		{{- range .Headers }}
 			{{ .VarName }} {{ .TypeRef }}
 		{{- end }}
+		{{- range .Cookies }}
+			{{ .VarName }} {{ .TypeRef }}
+		{{- end }}
 		{{- if and .MustValidate (or (not .ServerBody) .Multipart) }}
 			err error
 		{{- end }}
+		{{- if .Cookies }}
+			c *http.Cookie
+		{{- end }}
 		{{- if .PathParams }}
 
 			params = mux.Vars(r)
@@ -825,6 +831,59 @@ const requestParamsHeadersT = `{{- define "request_params_headers" }}
 		{{ .Validate }}
 	{{- end }}
 {{- end }}
+
+{{- range .Cookies }}
+	c, err = r.Cookie("{{ .Name }}")
+	{{- if and (or (eq .Type.Name "string") (eq .Type.Name "any")) .Required }}
+		if err == http.ErrNoCookie {
+			err = goa.MergeErrors(err, goa.MissingFieldError("{{ .Name }}", "cookie"))
+		} else {
+			{{ .VarName }} = c.Value
+		}
+
+	{{- else if (or (eq .Type.Name "string") (eq .Type.Name "any")) }}
+		var {{ .VarName }}Raw string
+		if c != nil {
+			{{ .VarName }}Raw = c.Value
+		}
+		if {{ .VarName }}Raw != "" {
+			{{ .VarName }} = {{ if and (eq .Type.Name "string") .Pointer }}&{{ end }}{{ .VarName }}Raw
+		}
+		{{- if .DefaultValue }} else {
+			{{ .VarName }} = {{ if eq .Type.Name "string" }}{{ printf "%q" .DefaultValue }}{{ else }}{{ printf "%#v" .DefaultValue }}{{ end }}
+		}
+		{{- end }}
+
+	{{- else }}{{/* not string and not any */}}
+	{
+		var {{ .VarName }}Raw string
+		if c != nil {
+			{{ .VarName }}Raw = c.Value
+		}
+		{{- if .Required }}
+		if {{ .VarName }}Raw == "" {
+			err = goa.MergeErrors(err, goa.MissingFieldError("{{ .Name }}", "cookie"))
+		}
+		{{- else if .DefaultValue }}
+		if {{ .VarName }}Raw == "" {
+			{{ .VarName }} = {{ printf "%#v" .DefaultValue }}
+		}
+		{{- end }}
+
+		{{- if .DefaultValue }}else {
+		{{- else if not .Required }}
+		if {{ .VarName }}Raw != "" {
+		{{- end }}
+		{{- template "type_conversion" . }}
+		{{- if or .DefaultValue (not .Required) }}
+		}
+		{{- end }}
+	}
+	{{- end }}
+	{{- if .Validate }}
+		{{ .Validate }}
+	{{- end }}
+{{- end }}
 {{- end }}
 {{- end }}
 
@@ -1183,6 +1242,54 @@ const responseT = `{{ define "response" -}}
 
 	{{- end }}
 
+	{{- range .Cookies }}
+		{{- $initDef := and (or .FieldPointer .Slice) .DefaultValue }}
+		{{- $checkNil := and (or .FieldPointer .Slice (eq .Type.Name "bytes") (eq .Type.Name "any") $initDef) }}
+		{{- if $checkNil }}
+	if res.{{ if $.ViewedResult }}Projected.{{ end }}{{ .FieldName }} != nil {
+		{{- end }}
+
+		{{- if eq .Type.Name "string" }}
+	{{ .VarName }} := {{ if or .FieldPointer $.ViewedResult }}*{{ end }}res{{ if $.ViewedResult }}.Projected{{ end }}{{ if .FieldName }}.{{ .FieldName }}{{ end }}
+		{{- else }}
+			{{- if isAliased .FieldType }}
+	{{ .VarName }}raw := {{ goTypeRef .Type }}({{ if .FieldPointer }}*{{ end }}res{{ if $.ViewedResult }}.Projected{{ end }}{{ if .FieldName }}.{{ .FieldName }}{{ end }})
+	{{ template "header_conversion" (headerConversionData .Type (printf "%sraw" .VarName) true .VarName) }}
+			{{- else }}
+	{{ .VarName }}raw := res{{ if $.ViewedResult }}.Projected{{ end }}{{ if .FieldName }}.{{ .FieldName }}{{ end }}
+	{{ template "header_conversion" (headerConversionData .Type (printf "%sraw" .VarName) (not .FieldPointer) .VarName) }}
+			{{- end }}
+		{{- end }}
+
+		{{- if $initDef }}
+	{{ if $checkNil }} } else { {{ else }}if res{{ if $.ViewedResult }}.Projected{{ end }}.{{ .FieldName }} == nil { {{ end }}
+		{{ .VarName }} := "{{ printValue .Type .DefaultValue }}"
+		{{- end }}
+		http.SetCookie(w, &http.Cookie{
+			Name: {{ printf "%q" .Name }},
+			Value: {{ .VarName }},
+			{{- if .MaxAge }}
+			MaxAge: {{ .MaxAge }},
+			{{- end }}
+			{{- if .Path }}
+			Path: {{ .Path }},
+			{{- end }}
+			{{- if .Domain }}
+			Domain: {{ .Domain }},
+			{{- end }}
+			{{- if .Secure }}
+			Secure: true,
+			{{- end }}
+			{{- if .HTTPOnly }}
+			HttpOnly: true,
+			{{- end }}
+		})
+		{{- if or $checkNil $initDef }}
+	}
+		{{- end }}
+
+	{{- end }}
+
 	{{- if .ErrorHeader }}
 	w.Header().Set("goa-error", {{ printf "%q" .ErrorHeader }})
 	{{- end }}
@@ -1249,7 +1356,7 @@ func {{ .InitName }}(mux goahttp.Muxer, {{ .VarName }} {{ .FuncName }}) func(r *
 			if err := {{ .VarName }}(mr, p); err != nil {
 				return err
 			}
-			{{- template "request_params_headers" .Payload.Request }}
+			{{- template "request_elements" .Payload.Request }}
 			{{- if .Payload.Request.MustValidate }}
 			if err != nil {
 				return err
@@ -1258,7 +1365,7 @@ func {{ .InitName }}(mux goahttp.Muxer, {{ .VarName }} {{ .FuncName }}) func(r *
 			{{- if .Payload.Request.PayloadInit }}
 				{{- range .Payload.Request.PayloadInit.ServerArgs }}
 					{{- if .FieldName }}
-			(*p).{{ .FieldName }} = {{ if and (not .Pointer) .FieldPointer }}&{{ end }}{{ .Name }}
+			(*p).{{ .FieldName }} = {{ if and (not .Pointer) .FieldPointer }}&{{ end }}{{ .VarName }}
 					{{- end }}
 				{{- end }}
 			{{- end }}
@@ -1266,4 +1373,4 @@ func {{ .InitName }}(mux goahttp.Muxer, {{ .VarName }} {{ .FuncName }}) func(r *
 		})
 	}
 }
-` + requestParamsHeadersT
+` + requestElementsT
diff --git a/http/codegen/server_types.go b/http/codegen/server_types.go
index 04975365..7724ddd2 100644
--- a/http/codegen/server_types.go
+++ b/http/codegen/server_types.go
@@ -221,7 +221,7 @@ func fieldCode(init *InitData, typ string) string {
 	initArgs := make([]*codegen.InitArgData, len(args))
 	for i, arg := range args {
 		initArgs[i] = &codegen.InitArgData{
-			Name:         arg.Name,
+			Name:         arg.VarName,
 			Pointer:      arg.Pointer,
 			Type:         arg.Type,
 			FieldName:    arg.FieldName,
@@ -245,7 +245,7 @@ type {{ .VarName }} {{ .Def }}
 
 // input: InitData
 const serverTypeInitT = `{{ comment .Description }}
-func {{ .Name }}({{- range .ServerArgs }}{{ .Name }} {{ .TypeRef }}, {{ end }}) {{ .ReturnTypeRef }} {
+func {{ .Name }}({{- range .ServerArgs }}{{ .VarName }} {{ .TypeRef }}, {{ end }}) {{ .ReturnTypeRef }} {
 {{- if .ServerCode }}
 	{{ .ServerCode }}
 	{{- if .ReturnTypeAttribute }}
@@ -266,7 +266,7 @@ func {{ .Name }}({{- range .ServerArgs }}{{ .Name }} {{ .TypeRef }}, {{ end }})
 
 // input: InitData
 const serverBodyInitT = `{{ comment .Description }}
-func {{ .Name }}({{ range .ServerArgs }}{{ .Name }} {{.TypeRef }}, {{ end }}) {{ .ReturnTypeRef }} {
+func {{ .Name }}({{ range .ServerArgs }}{{ .VarName }} {{.TypeRef }}, {{ end }}) {{ .ReturnTypeRef }} {
 	{{ .ServerCode }}
 	return body
 }
diff --git a/http/codegen/service_data.go b/http/codegen/service_data.go
index f7ea71d5..6ff0ba20 100644
--- a/http/codegen/service_data.go
+++ b/http/codegen/service_data.go
@@ -220,7 +220,7 @@ type (
 		// MustInit indicates if a variable holding the result type must be
 		// initialized. It is used by server response encoder to initialize
 		// the result variable only if there are multiple responses, or the
-		// response has a body or a header.
+		// response has a body, a header or a cookie.
 		MustInit bool
 	}
 
@@ -256,6 +256,9 @@ type (
 		// Headers contains the HTTP request headers used to build the
 		// method payload.
 		Headers []*HeaderData
+		// Cookies contains the HTTP request cookies used to build the
+		// method payload.
+		Cookies []*CookieData
 		// ServerBody describes the request body type used by server
 		// code. The type is generated using pointers for all fields so
 		// that it can be validated.
@@ -287,9 +290,10 @@ type (
 		StatusCode string
 		// Description is the response description.
 		Description string
-		// Headers provides information about the headers in the
-		// response.
+		// Headers provides information about the HTTP response headers.
 		Headers []*HeaderData
+		// Cookies provides information about the HTTP response cookies.
+		Cookies []*CookieData
 		// ContentType contains the value of the response
 		// "Content-Type" header.
 		ContentType string
@@ -379,42 +383,47 @@ type (
 		ReturnIsPrimitivePointer bool
 	}
 
-	// InitArgData represents a single constructor argument.
-	InitArgData struct {
-		// Name is the argument name.
-		Name string
-		// Description is the argument description.
+	// AttributeData contains the information needed to generate the code
+	// related to a specific payload or result attribute.
+	AttributeData struct {
+		// VarName is the name of the variable that holds the attribute value.
+		VarName string
+		// Pointer is true if the attribute value is a pointer.
+		Pointer bool
+		// Required is true if the attribute is required in the payload or result.
+		Required bool
+		// Type is the attribute type.
+		Type expr.DataType
+		// TypeName is the generated attribute type name.
+		TypeName string
+		// TypeRef is the generated attribute type reference.
+		TypeRef string
+		// Description is the attribute description as defined in the design.
 		Description string
-		// Reference to the argument, e.g. "&body".
-		Ref string
 		// FieldName is the name of the data structure field that should
-		// be initialized with the argument if any.
+		// be initialized with the value if any.
 		FieldName string
+		// FieldType is the type of the data structure field that should be
+		// initialized with the attribute vaue or read into the attribute value.
+		FieldType expr.DataType
 		// FieldPointer if true indicates that the data structure field is a
 		// pointer.
 		FieldPointer bool
-		// FieldType is the type of the data structure field that should
-		// be initialized with the argument if any.
-		FieldType expr.DataType
-		// TypeName is the argument type name.
-		TypeName string
-		// TypeRef is the argument type reference.
-		TypeRef string
-		// Type is the argument type. It is never an aliased user type.
-		Type expr.DataType
-		// Pointer is true if a pointer to the arg should be used.
-		Pointer bool
-		// Required is true if the arg is required to build the payload.
-		Required bool
-		// DefaultValue is the default value of the arg.
+		// DefaultValue is the default value of the attribute.
 		DefaultValue interface{}
-		// Validate contains the validation code for the argument
-		// value if any.
+		// Validate contains the validation code for the attribute value if any.
 		Validate string
-		// Example is a example value
+		// Example is an example attribute value
 		Example interface{}
 	}
 
+	// InitArgData represents a single constructor argument.
+	InitArgData struct {
+		*AttributeData
+		// Reference to the argument, e.g. "&body".
+		Ref string
+	}
+
 	// RouteData describes a route.
 	RouteData struct {
 		// Verb is the HTTP method.
@@ -426,53 +435,30 @@ type (
 		PathInit *InitData
 	}
 
-	// ParamData describes a HTTP request parameter.
-	ParamData struct {
-		// Name is the name of the mapping to the actual variable name.
+	// Element defines the common fields needed to generate HTTP request and
+	// response elements including headers, parameters and cookies.
+	Element struct {
+		*AttributeData
+		// Name is the name of the HTTP element (header name, query string name
+		// or cookie name)
 		Name string
 		// AttributeName is the name of the corresponding attribute.
 		AttributeName string
-		// Description is the parameter description
-		Description string
-		// FieldName is the name of the struct field that holds the
-		// param value.
-		FieldName string
-		// FieldPointer if true indicates that the struct field that holds the
-		// param value is a pointer.
-		FieldPointer bool
-		// FieldType is the type of the struct field.
-		FieldType expr.DataType
-		// VarName is the name of the Go variable used to read or
-		// convert the param value.
-		VarName string
-		// ServiceField is true if there is a corresponding attribute in
-		// the service types.
-		ServiceField bool
-		// Type is the datatype of the variable.
-		Type expr.DataType
-		// TypeName is the name of the type.
-		TypeName string
-		// TypeRef is the reference to the type.
-		TypeRef string
-		// Required is true if the param is required.
-		Required bool
-		// Pointer is true if and only the param variable is a pointer.
-		Pointer bool
-		// StringSlice is true if the param type is array of strings.
+		// StringSlice is true if the attribute type is array of strings.
 		StringSlice bool
-		// Slice is true if the param type is an array.
+		// Slice is true if the attribute type is an array.
 		Slice bool
+	}
+
+	// ParamData describes a HTTP request parameter (query string or path
+	// parameter).
+	ParamData struct {
+		*Element
 		// MapStringSlice is true if the param type is a map of string
 		// slice.
 		MapStringSlice bool
 		// Map is true if the param type is a map.
 		Map bool
-		// Validate contains the validation code if any.
-		Validate string
-		// DefaultValue contains the default value if any.
-		DefaultValue interface{}
-		// Example is an example value.
-		Example interface{}
 		// MapQueryParams indicates that the query params must be mapped
 		// to the entire payload (empty string) or a payload attribute
 		// (attribute name).
@@ -481,46 +467,24 @@ type (
 
 	// HeaderData describes a HTTP request or response header.
 	HeaderData struct {
-		// Name is the name of the header key.
-		Name string
-		// AttributeName is the name of the corresponding attribute.
-		AttributeName string
-		// Description is the header description.
-		Description string
+		*Element
 		// CanonicalName is the canonical header key.
 		CanonicalName string
-		// FieldName is the name of the struct field that holds the
-		// header value if any, empty string otherwise.
-		FieldName string
-		// FieldType is the type of the struct field.
-		FieldType expr.DataType
-		// FieldPointer if true indicates that the struct field that holds the
-		// header value is a pointer.
-		FieldPointer bool
-		// VarName is the name of the Go variable used to read or
-		// convert the header value.
-		VarName string
-		// TypeName is the name of the type.
-		TypeName string
-		// TypeRef is the reference to the type.
-		TypeRef string
-		// Required is true if the header is required.
-		Required bool
-		// Pointer is true if and only the param variable is a pointer.
-		Pointer bool
-		// StringSlice is true if the param type is array of strings.
-		StringSlice bool
-		// Slice is true if the param type is an array.
-		Slice bool
-		// Type describes the datatype of the variable value. Mainly
-		// used for conversion.
-		Type expr.DataType
-		// Validate contains the validation code if any.
-		Validate string
-		// DefaultValue contains the default value if any.
-		DefaultValue interface{}
-		// Example is an example value.
-		Example interface{}
+	}
+
+	// CookieData describes a HTTP request or response cookie.
+	CookieData struct {
+		*Element
+		// MaxAge is the cookie "max-age" attribute.
+		MaxAge string
+		// Path is the cookie "path" attribute.
+		Path string
+		// Domain is the cookie "domain" attribute.
+		Domain string
+		// Secure sets the cookie "secure" attribute to "Secure" if true.
+		Secure bool
+		// HTTPOnly sets the cookie "http-only" attribute to "HttpOnly" if true.
+		HTTPOnly bool
 	}
 
 	// TypeData contains the data needed to render a type definition.
@@ -565,54 +529,6 @@ type (
 		// encoder/decoder.
 		Payload *PayloadData
 	}
-
-	// WebSocketData contains the data needed to render struct type that
-	// implements the server and client stream interfaces.
-	WebSocketData struct {
-		// VarName is the name of the struct.
-		VarName string
-		// Type is type of the stream (server or client).
-		Type string
-		// Interface is the fully qualified name of the interface that
-		// the struct implements.
-		Interface string
-		// Endpoint is endpoint data that defines streaming
-		// payload/result.
-		Endpoint *EndpointData
-		// Payload is the streaming payload type sent via the stream.
-		Payload *TypeData
-		// Response is the successful response data for the streaming
-		// endpoint.
-		Response *ResponseData
-		// SendName is the name of the send function.
-		SendName string
-		// SendDesc is the description for the send function.
-		SendDesc string
-		// SendTypeName is the fully qualified type name sent through
-		// the stream.
-		SendTypeName string
-		// SendTypeRef is the fully qualified type ref sent through the
-		// stream.
-		SendTypeRef string
-		// RecvName is the name of the receive function.
-		RecvName string
-		// RecvDesc is the description for the recv function.
-		RecvDesc string
-		// RecvTypeName is the fully qualified type name received from
-		// the stream.
-		RecvTypeName string
-		// RecvTypeRef is the fully qualified type ref received from the
-		// stream.
-		RecvTypeRef string
-		// MustClose indicates whether to generate the Close() function
-		// for the stream.
-		MustClose bool
-		// PkgName is the service package name.
-		PkgName string
-		// Kind is the kind of the stream (payload, result or
-		// bidirectional).
-		Kind expr.StreamKind
-	}
 )
 
 // Get retrieves the transport data for the service with the given name
@@ -719,18 +635,20 @@ func (d ServicesData) analyze(hs *expr.HTTPServiceExpr) *ServiceData {
 							vcode = codegen.RecursiveValidationCode(att, ctx, true, name)
 						}
 						initArgs[j] = &InitArgData{
-							Name:        name,
-							Description: att.Description,
-							Ref:         name,
-							FieldName:   codegen.Goify(arg, true),
-							FieldType:   patt.Type,
-							TypeName:    rd.Scope.GoTypeName(att),
-							TypeRef:     rd.Scope.GoTypeRef(att),
-							Type:        att.Type,
-							Pointer:     pointer,
-							Required:    true,
-							Example:     att.Example(expr.Root.API.Random()),
-							Validate:    vcode,
+							Ref: name,
+							AttributeData: &AttributeData{
+								VarName:     name,
+								Description: att.Description,
+								FieldName:   codegen.Goify(arg, true),
+								FieldType:   patt.Type,
+								TypeName:    rd.Scope.GoTypeName(att),
+								TypeRef:     rd.Scope.GoTypeRef(att),
+								Type:        att.Type,
+								Pointer:     pointer,
+								Required:    true,
+								Example:     att.Example(expr.Root.API.Random()),
+								Validate:    vcode,
+							},
 						}
 					}
 
@@ -800,7 +718,7 @@ func (d ServicesData) analyze(hs *expr.HTTPServiceExpr) *ServiceData {
 
 		var requestEncoder string
 		{
-			if payload.Request.ClientBody != nil || len(payload.Request.Headers) > 0 || len(payload.Request.QueryParams) > 0 || basch != nil {
+			if payload.Request.ClientBody != nil || len(payload.Request.Headers) > 0 || len(payload.Request.QueryParams) > 0 || len(payload.Request.Cookies) > 0 || basch != nil {
 				requestEncoder = fmt.Sprintf("Encode%sRequest", ep.VarName)
 			}
 		}
@@ -818,8 +736,8 @@ func (d ServicesData) analyze(hs *expr.HTTPServiceExpr) *ServiceData {
 				s.Unique("c") // 'c' is reserved as the client's receiver name.
 				for _, ca := range routes[0].PathInit.ClientArgs {
 					if ca.FieldName != "" {
-						ca.Name = s.Unique(ca.Name)
-						ca.Ref = ca.Name
+						ca.VarName = s.Unique(ca.VarName)
+						ca.Ref = ca.VarName
 						args = append(args, ca)
 					}
 				}
@@ -844,7 +762,7 @@ func (d ServicesData) analyze(hs *expr.HTTPServiceExpr) *ServiceData {
 			if err := requestInitTmpl.Execute(&buf, data); err != nil {
 				panic(err) // bug
 			}
-			clientArgs := []*InitArgData{{Name: "v", Ref: "v", TypeRef: "interface{}"}}
+			clientArgs := []*InitArgData{{Ref: "v", AttributeData: &AttributeData{VarName: "v", TypeRef: "interface{}"}}}
 			requestInit = &InitData{
 				Name:        name,
 				Description: fmt.Sprintf("%s instantiates a HTTP request object with method and path set to call the %q service %q endpoint", name, svc.Name, ep.Name),
@@ -1027,6 +945,7 @@ func buildPayloadData(e *expr.HTTPEndpointExpr, sd *ServiceData) *PayloadData {
 			paramsData     = extractPathParams(e.PathParams(), payload, sd.Scope)
 			queryData      = extractQueryParams(e.QueryParams(), payload, sd.Scope)
 			headersData    = extractHeaders(e.Headers, payload, svcctx, sd.Scope)
+			cookiesData    = extractCookies(e.Cookies, payload, svcctx, sd.Scope)
 			origin         string
 
 			mustValidate bool
@@ -1047,19 +966,23 @@ func buildPayloadData(e *expr.HTTPEndpointExpr, sd *ServiceData) *PayloadData {
 				}
 				varn := codegen.Goify(name, false)
 				mapQueryParam = &ParamData{
-					Name:           name,
-					VarName:        varn,
-					FieldName:      fieldName,
-					FieldType:      pAtt.Type,
-					Required:       required,
-					Type:           pAtt.Type,
-					TypeName:       sd.Scope.GoTypeName(pAtt),
-					TypeRef:        sd.Scope.GoTypeRef(pAtt),
-					Map:            expr.AsMap(payload.Type) != nil,
-					Validate:       codegen.RecursiveValidationCode(pAtt, httpsvrctx, required, varn),
-					DefaultValue:   pAtt.DefaultValue,
-					Example:        pAtt.Example(expr.Root.API.Random()),
 					MapQueryParams: e.MapQueryParams,
+					Map:            expr.AsMap(payload.Type) != nil,
+					Element: &Element{
+						Name: name,
+						AttributeData: &AttributeData{
+							VarName:      varn,
+							FieldName:    fieldName,
+							FieldType:    pAtt.Type,
+							Required:     required,
+							Type:         pAtt.Type,
+							TypeName:     sd.Scope.GoTypeName(pAtt),
+							TypeRef:      sd.Scope.GoTypeRef(pAtt),
+							Validate:     codegen.RecursiveValidationCode(pAtt, httpsvrctx, required, varn),
+							DefaultValue: pAtt.DefaultValue,
+							Example:      pAtt.Example(expr.Root.API.Random()),
+						},
+					},
 				}
 				queryData = append(queryData, mapQueryParam)
 			}
@@ -1089,6 +1012,14 @@ func buildPayloadData(e *expr.HTTPEndpointExpr, sd *ServiceData) *PayloadData {
 					}
 				}
 			}
+			if !mustValidate {
+				for _, c := range cookiesData {
+					if c.Validate != "" || c.Required || needConversion(c.Type) {
+						mustValidate = true
+						break
+					}
+				}
+			}
 			if e.Body.Type != expr.Empty {
 				// If design uses Body("name") syntax we need to use the
 				// corresponding attribute in the result type for body
@@ -1102,6 +1033,7 @@ func buildPayloadData(e *expr.HTTPEndpointExpr, sd *ServiceData) *PayloadData {
 			PathParams:   paramsData,
 			QueryParams:  queryData,
 			Headers:      headersData,
+			Cookies:      cookiesData,
 			ServerBody:   serverBodyData,
 			ClientBody:   clientBodyData,
 			PayloadAttr:  codegen.Goify(origin, true),
@@ -1114,7 +1046,7 @@ func buildPayloadData(e *expr.HTTPEndpointExpr, sd *ServiceData) *PayloadData {
 	var init *InitData
 	if needInit(payload.Type) {
 		// generate constructor function to transform request body,
-		// params, and headers into the method payload type
+		// params, headers and cookies into the method payload type
 		var (
 			name       string
 			desc       string
@@ -1147,76 +1079,105 @@ func buildPayloadData(e *expr.HTTPEndpointExpr, sd *ServiceData) *PayloadData {
 				}
 			}
 			serverArgs = []*InitArgData{{
-				Name:     "body",
-				Ref:      sd.Scope.GoVar("body", body),
-				TypeName: sd.Scope.GoTypeName(e.Body),
-				TypeRef:  sd.Scope.GoTypeRef(e.Body),
-				Type:     body,
-				Required: true,
-				Example:  e.Body.Example(expr.Root.API.Random()),
-				Validate: svcode,
+				Ref: sd.Scope.GoVar("body", body),
+				AttributeData: &AttributeData{
+					VarName:  "body",
+					TypeName: sd.Scope.GoTypeName(e.Body),
+					TypeRef:  sd.Scope.GoTypeRef(e.Body),
+					Type:     body,
+					Required: true,
+					Example:  e.Body.Example(expr.Root.API.Random()),
+					Validate: svcode,
+				},
 			}}
 			clientArgs = []*InitArgData{{
-				Name:     "body",
-				Ref:      sd.Scope.GoVar("body", body),
-				TypeName: sd.Scope.GoTypeName(e.Body),
-				TypeRef:  sd.Scope.GoTypeRef(e.Body),
-				Type:     body,
-				Required: true,
-				Example:  e.Body.Example(expr.Root.API.Random()),
-				Validate: cvcode,
+				Ref: sd.Scope.GoVar("body", body),
+				AttributeData: &AttributeData{
+					VarName:  "body",
+					TypeName: sd.Scope.GoTypeName(e.Body),
+					TypeRef:  sd.Scope.GoTypeRef(e.Body),
+					Type:     body,
+					Required: true,
+					Example:  e.Body.Example(expr.Root.API.Random()),
+					Validate: cvcode,
+				},
 			}}
 		}
 		var args []*InitArgData
 		for _, p := range request.PathParams {
 			args = append(args, &InitArgData{
-				Name:         p.VarName,
-				Description:  p.Description,
-				Ref:          p.VarName,
-				FieldName:    p.FieldName,
-				FieldPointer: p.FieldPointer,
-				FieldType:    p.FieldType,
-				TypeName:     p.TypeName,
-				TypeRef:      p.TypeRef,
-				Type:         p.Type,
-				Pointer:      p.Pointer,
-				Required:     p.Required,
-				Validate:     p.Validate,
-				Example:      p.Example,
+				Ref: p.VarName,
+				AttributeData: &AttributeData{
+					VarName:      p.VarName,
+					Description:  p.Description,
+					FieldName:    p.FieldName,
+					FieldPointer: p.FieldPointer,
+					FieldType:    p.FieldType,
+					TypeName:     p.TypeName,
+					TypeRef:      p.TypeRef,
+					Type:         p.Type,
+					Pointer:      p.Pointer,
+					Required:     p.Required,
+					Validate:     p.Validate,
+					Example:      p.Example,
+				},
 			})
 		}
 		for _, p := range request.QueryParams {
 			args = append(args, &InitArgData{
-				Name:         p.VarName,
-				Ref:          p.VarName,
-				FieldName:    p.FieldName,
-				FieldPointer: p.FieldPointer,
-				FieldType:    p.FieldType,
-				TypeName:     p.TypeName,
-				TypeRef:      p.TypeRef,
-				Type:         p.Type,
-				Pointer:      p.Pointer,
-				Required:     p.Required,
-				DefaultValue: p.DefaultValue,
-				Validate:     p.Validate,
-				Example:      p.Example,
+				Ref: p.VarName,
+				AttributeData: &AttributeData{
+					VarName:      p.VarName,
+					FieldName:    p.FieldName,
+					FieldPointer: p.FieldPointer,
+					FieldType:    p.FieldType,
+					TypeName:     p.TypeName,
+					TypeRef:      p.TypeRef,
+					Type:         p.Type,
+					Pointer:      p.Pointer,
+					Required:     p.Required,
+					DefaultValue: p.DefaultValue,
+					Validate:     p.Validate,
+					Example:      p.Example,
+				},
 			})
 		}
 		for _, h := range request.Headers {
 			args = append(args, &InitArgData{
-				Name:         h.VarName,
-				Ref:          h.VarName,
-				FieldName:    h.FieldName,
-				FieldPointer: h.FieldPointer,
-				FieldType:    h.FieldType,
-				TypeName:     h.TypeName,
-				TypeRef:      h.TypeRef,
-				Type:         h.Type,
-				Pointer:      h.Pointer,
-				Required:     h.Required,
-				DefaultValue: h.DefaultValue,
-				Validate:     h.Validate,
-				Example:      h.Example,
+				Ref: h.VarName,
+				AttributeData: &AttributeData{
+					VarName:      h.VarName,
+					FieldName:    h.FieldName,
+					FieldPointer: h.FieldPointer,
+					FieldType:    h.FieldType,
+					TypeName:     h.TypeName,
+					TypeRef:      h.TypeRef,
+					Type:         h.Type,
+					Pointer:      h.Pointer,
+					Required:     h.Required,
+					DefaultValue: h.DefaultValue,
+					Validate:     h.Validate,
+					Example:      h.Example,
+				},
+			})
+		}
+		for _, c := range request.Cookies {
+			args = append(args, &InitArgData{
+				Ref: c.VarName,
+				AttributeData: &AttributeData{
+					VarName:      c.VarName,
+					FieldName:    c.FieldName,
+					FieldPointer: c.FieldPointer,
+					FieldType:    c.FieldType,
+					TypeName:     c.TypeName,
+					TypeRef:      c.TypeRef,
+					Type:         c.Type,
+					Pointer:      c.Pointer,
+					Required:     c.Required,
+					DefaultValue: c.DefaultValue,
+					Validate:     c.Validate,
+					Example:      c.Example,
+				},
 			})
 		}
 		serverArgs = append(serverArgs, args...)
@@ -1235,19 +1196,21 @@ func buildPayloadData(e *expr.HTTPEndpointExpr, sd *ServiceData) *PayloadData {
 						uref = "*" + uref
 					}
 					uarg := &InitArgData{
-						Name:         sc.UsernameAttr,
-						FieldName:    sc.UsernameField,
-						FieldPointer: sc.UsernamePointer,
-						FieldType:    uatt.Type,
-						Description:  uatt.Description,
-						Ref:          sc.UsernameAttr,
-						Required:     sc.UsernameRequired,
-						TypeName:     svc.Scope.GoTypeName(uatt),
-						TypeRef:      uref,
-						Type:         uatt.Type,
-						Pointer:      sc.UsernamePointer,
-						Validate:     codegen.RecursiveValidationCode(uatt, httpsvrctx, sc.UsernameRequired, sc.UsernameAttr),
-						Example:      uatt.Example(expr.Root.API.Random()),
+						Ref: sc.UsernameAttr,
+						AttributeData: &AttributeData{
+							VarName:      sc.UsernameAttr,
+							FieldName:    sc.UsernameField,
+							FieldPointer: sc.UsernamePointer,
+							FieldType:    uatt.Type,
+							Description:  uatt.Description,
+							Required:     sc.UsernameRequired,
+							TypeName:     svc.Scope.GoTypeName(uatt),
+							TypeRef:      uref,
+							Type:         uatt.Type,
+							Pointer:      sc.UsernamePointer,
+							Validate:     codegen.RecursiveValidationCode(uatt, httpsvrctx, sc.UsernameRequired, sc.UsernameAttr),
+							Example:      uatt.Example(expr.Root.API.Random()),
+						},
 					}
 					patt := e.MethodExpr.Payload.Find(sc.PasswordAttr)
 					pref := svc.Scope.GoTypeRef(patt)
@@ -1255,19 +1218,21 @@ func buildPayloadData(e *expr.HTTPEndpointExpr, sd *ServiceData) *PayloadData {
 						pref = "*" + pref
 					}
 					parg := &InitArgData{
-						Name:         sc.PasswordAttr,
-						FieldName:    sc.PasswordField,
-						FieldPointer: sc.PasswordPointer,
-						FieldType:    patt.Type,
-						Description:  patt.Description,
-						Ref:          sc.PasswordAttr,
-						Required:     sc.PasswordRequired,
-						TypeName:     svc.Scope.GoTypeName(patt),
-						TypeRef:      pref,
-						Type:         patt.Type,
-						Pointer:      sc.PasswordPointer,
-						Validate:     codegen.RecursiveValidationCode(patt, httpsvrctx, sc.PasswordRequired, sc.PasswordAttr),
-						Example:      patt.Example(expr.Root.API.Random()),
+						Ref: sc.PasswordAttr,
+						AttributeData: &AttributeData{
+							VarName:      sc.PasswordAttr,
+							FieldName:    sc.PasswordField,
+							FieldPointer: sc.PasswordPointer,
+							FieldType:    patt.Type,
+							Description:  patt.Description,
+							Required:     sc.PasswordRequired,
+							TypeName:     svc.Scope.GoTypeName(patt),
+							TypeRef:      pref,
+							Type:         patt.Type,
+							Pointer:      sc.PasswordPointer,
+							Validate:     codegen.RecursiveValidationCode(patt, httpsvrctx, sc.PasswordRequired, sc.PasswordAttr),
+							Example:      patt.Example(expr.Root.API.Random()),
+						},
 					}
 					cliArgs = []*InitArgData{uarg, parg}
 					done = true
@@ -1361,8 +1326,10 @@ func buildPayloadData(e *expr.HTTPEndpointExpr, sd *ServiceData) *PayloadData {
 				returnValue = codegen.Goify((*o)[0].Name, false)
 			} else if o := expr.AsObject(e.Headers.Type); o != nil && len(*o) > 0 {
 				returnValue = codegen.Goify((*o)[0].Name, false)
+			} else if o := expr.AsObject(e.Cookies.Type); o != nil && len(*o) > 0 {
+				returnValue = codegen.Goify((*o)[0].Name, false)
 			} else if e.MapQueryParams != nil && *e.MapQueryParams == "" {
-				returnValue = mapQueryParam.Name
+				returnValue = mapQueryParam.VarName
 			}
 		}
 	}
@@ -1411,8 +1378,8 @@ func buildResultData(e *expr.HTTPEndpointExpr, sd *ServiceData) *ResultData {
 		}
 		responses = buildResponses(e, result, viewed, sd)
 		for _, r := range responses {
-			// response has a body or headers or tag
-			if len(r.ServerBody) > 0 || len(r.Headers) > 0 || r.TagName != "" {
+			// response has a body, headers, cookies or tag
+			if len(r.ServerBody) > 0 || len(r.Headers) > 0 || len(r.Cookies) > 0 || r.TagName != "" {
 				mustInit = true
 			}
 		}
@@ -1427,10 +1394,9 @@ func buildResultData(e *expr.HTTPEndpointExpr, sd *ServiceData) *ResultData {
 	}
 }
 
-// buildResponses builds the response data for all the responses in the
-// endpoint expression. The response headers and body for each response
-// are inferred from the method's result expression if not specified
-// explicitly.
+// buildResponses builds the response data for all the responses in the endpoint
+// expression. The response headers, cookies and body for each response are
+// inferred from the method's result expression if not specified explicitly.
 //
 // viewed parameter indicates if the method result uses views.
 func buildResponses(e *expr.HTTPEndpointExpr, result *expr.AttributeExpr, viewed bool, sd *ServiceData) []*ResponseData {
@@ -1460,6 +1426,7 @@ func buildResponses(e *expr.HTTPEndpointExpr, result *expr.AttributeExpr, viewed
 			}
 			var (
 				headersData    []*HeaderData
+				cookiesData    []*CookieData
 				serverBodyData []*TypeData
 				clientBodyData *TypeData
 				init           *InitData
@@ -1470,6 +1437,7 @@ func buildResponses(e *expr.HTTPEndpointExpr, result *expr.AttributeExpr, viewed
 			)
 			{
 				headersData = extractHeaders(resp.Headers, result, svcctx, scope)
+				cookiesData = extractCookies(resp.Cookies, result, svcctx, scope)
 				if resp.Body.Type != expr.Empty {
 					// If design uses Body("name") syntax we need to use the
 					// corresponding attribute in the result type for body
@@ -1525,9 +1493,15 @@ func buildResponses(e *expr.HTTPEndpointExpr, result *expr.AttributeExpr, viewed
 						break
 					}
 				}
+				for _, c := range cookiesData {
+					if c.Validate != "" || c.Required || needConversion(c.Type) {
+						mustValidate = true
+						break
+					}
+				}
 				if needInit(result.Type) {
-					// generate constructor function to transform response body
-					// and headers into the method result type
+					// generate constructor function to transform response body,
+					// headers and cookies into the method result type
 					var (
 						name       string
 						desc       string
@@ -1574,10 +1548,12 @@ func buildResponses(e *expr.HTTPEndpointExpr, result *expr.AttributeExpr, viewed
 								}
 							}
 							clientArgs = []*InitArgData{{
-								Name:     "body",
-								Ref:      ref,
-								TypeRef:  sd.Scope.GoTypeRef(resp.Body),
-								Validate: vcode,
+								Ref: ref,
+								AttributeData: &AttributeData{
+									VarName:  "body",
+									TypeRef:  sd.Scope.GoTypeRef(resp.Body),
+									Validate: vcode,
+								},
 							}}
 							// If the method result is a
 							// * result type - we unmarshal the client response body to the
@@ -1605,17 +1581,36 @@ func buildResponses(e *expr.HTTPEndpointExpr, result *expr.AttributeExpr, viewed
 						}
 						for _, h := range headersData {
 							clientArgs = append(clientArgs, &InitArgData{
-								Name:         h.VarName,
-								Ref:          h.VarName,
-								FieldName:    h.FieldName,
-								FieldPointer: h.FieldPointer,
-								FieldType:    h.FieldType,
-								Required:     h.Required,
-								Pointer:      h.Pointer,
-								TypeRef:      h.TypeRef,
-								Type:         h.Type,
-								Validate:     h.Validate,
-								Example:      h.Example,
+								Ref: h.VarName,
+								AttributeData: &AttributeData{
+									VarName:      h.VarName,
+									FieldName:    h.FieldName,
+									FieldPointer: h.FieldPointer,
+									FieldType:    h.FieldType,
+									Required:     h.Required,
+									Pointer:      h.Pointer,
+									TypeRef:      h.TypeRef,
+									Type:         h.Type,
+									Validate:     h.Validate,
+									Example:      h.Example,
+								},
+							})
+						}
+						for _, c := range cookiesData {
+							clientArgs = append(clientArgs, &InitArgData{
+								Ref: c.VarName,
+								AttributeData: &AttributeData{
+									VarName:      c.VarName,
+									FieldName:    c.FieldName,
+									FieldPointer: c.FieldPointer,
+									FieldType:    c.FieldType,
+									Required:     c.Required,
+									Pointer:      c.Pointer,
+									TypeRef:      c.TypeRef,
+									Type:         c.Type,
+									Validate:     c.Validate,
+									Example:      c.Example,
+								},
 							})
 						}
 					}
@@ -1649,6 +1644,7 @@ func buildResponses(e *expr.HTTPEndpointExpr, result *expr.AttributeExpr, viewed
 					StatusCode:   statusCodeToHTTPConst(resp.StatusCode),
 					Description:  resp.Description,
 					Headers:      headersData,
+					Cookies:      cookiesData,
 					ContentType:  resp.ContentType,
 					ServerBody:   serverBodyData,
 					ClientBody:   clientBodyData,
@@ -1672,9 +1668,8 @@ func buildResponses(e *expr.HTTPEndpointExpr, result *expr.AttributeExpr, viewed
 }
 
 // buildErrorsData builds the error data for all the error responses in the
-// endpoint expression. The response headers and body for each response
-// are inferred from the method's error expression if not specified
-// explicitly.
+// endpoint expression. The response headers, cookies and body for each response
+// are inferred from the method's error expression if not specified explicitly.
 func buildErrorsData(e *expr.HTTPEndpointExpr, sd *ServiceData) []*ErrorGroupData {
 	var (
 		svc        = sd.Service
@@ -1707,22 +1702,38 @@ func buildErrorsData(e *expr.HTTPEndpointExpr, sd *ServiceData) []*ErrorGroupDat
 						ref = "&body"
 					}
 					args = []*InitArgData{{
-						Name:    "body",
-						Ref:     ref,
-						TypeRef: sd.Scope.GoTypeRef(v.Response.Body),
+						Ref:           ref,
+						AttributeData: &AttributeData{VarName: "body", TypeRef: sd.Scope.GoTypeRef(v.Response.Body)},
 					}}
 				}
 				for _, h := range extractHeaders(v.Response.Headers, v.ErrorExpr.AttributeExpr, svcctx, sd.Scope) {
 					args = append(args, &InitArgData{
-						Name:         h.VarName,
-						Ref:          h.VarName,
-						FieldName:    h.FieldName,
-						FieldPointer: false,
-						FieldType:    h.FieldType,
-						TypeRef:      h.TypeRef,
-						Type:         h.Type,
-						Validate:     h.Validate,
-						Example:      h.Example,
+						Ref: h.VarName,
+						AttributeData: &AttributeData{
+							VarName:      h.VarName,
+							FieldName:    h.FieldName,
+							FieldPointer: false,
+							FieldType:    h.FieldType,
+							TypeRef:      h.TypeRef,
+							Type:         h.Type,
+							Validate:     h.Validate,
+							Example:      h.Example,
+						},
+					})
+				}
+				for _, c := range extractCookies(v.Response.Cookies, v.ErrorExpr.AttributeExpr, svcctx, sd.Scope) {
+					args = append(args, &InitArgData{
+						Ref: c.VarName,
+						AttributeData: &AttributeData{
+							VarName:      c.VarName,
+							FieldName:    c.FieldName,
+							FieldPointer: false,
+							FieldType:    c.FieldType,
+							TypeRef:      c.TypeRef,
+							Type:         c.Type,
+							Validate:     c.Validate,
+							Example:      c.Example,
+						},
 					})
 				}
 			}
@@ -1797,6 +1808,7 @@ func buildErrorsData(e *expr.HTTPEndpointExpr, sd *ServiceData) []*ErrorGroupDat
 			}
 
 			headers := extractHeaders(v.Response.Headers, v.ErrorExpr.AttributeExpr, svcctx, sd.Scope)
+			cookies := extractCookies(v.Response.Cookies, v.ErrorExpr.AttributeExpr, svcctx, sd.Scope)
 			var mustValidate bool
 			{
 				for _, h := range headers {
@@ -1805,6 +1817,12 @@ func buildErrorsData(e *expr.HTTPEndpointExpr, sd *ServiceData) []*ErrorGroupDat
 						break
 					}
 				}
+				for _, c := range cookies {
+					if c.Validate != "" || c.Required || needConversion(c.Type) {
+						mustValidate = true
+						break
+					}
+				}
 			}
 			var contentType string
 			if v.Response.ContentType != expr.ErrorResultIdentifier {
@@ -1814,6 +1832,7 @@ func buildErrorsData(e *expr.HTTPEndpointExpr, sd *ServiceData) []*ErrorGroupDat
 				StatusCode:   statusCodeToHTTPConst(v.Response.StatusCode),
 				Headers:      headers,
 				ContentType:  contentType,
+				Cookies:      cookies,
 				ErrorHeader:  v.Name,
 				ServerBody:   serverBodyData,
 				ClientBody:   clientBodyData,
@@ -1860,160 +1879,6 @@ func buildErrorsData(e *expr.HTTPEndpointExpr, sd *ServiceData) []*ErrorGroupDat
 	return vals
 }
 
-// initWebSocketData initializes the WebSocket related data in ed.
-func initWebSocketData(ed *EndpointData, e *expr.HTTPEndpointExpr, sd *ServiceData) {
-	var (
-		svrSendTypeName string
-		svrSendTypeRef  string
-		svrRecvTypeName string
-		svrRecvTypeRef  string
-		svrSendDesc     string
-		svrRecvDesc     string
-		svrPayload      *TypeData
-		cliSendDesc     string
-		cliRecvDesc     string
-		cliPayload      *TypeData
-
-		md     = ed.Method
-		svc    = sd.Service
-		svcctx = serviceContext(sd.Service.PkgName, sd.Service.Scope)
-	)
-	{
-		svrSendTypeName = ed.Result.Name
-		svrSendTypeRef = ed.Result.Ref
-		svrSendDesc = fmt.Sprintf("%s streams instances of %q to the %q endpoint websocket connection.", md.ServerStream.SendName, svrSendTypeName, md.Name)
-		cliRecvDesc = fmt.Sprintf("%s reads instances of %q from the %q endpoint websocket connection.", md.ClientStream.RecvName, svrSendTypeName, md.Name)
-		if e.MethodExpr.Stream == expr.ClientStreamKind || e.MethodExpr.Stream == expr.BidirectionalStreamKind {
-			svrRecvTypeName = sd.Scope.GoFullTypeName(e.MethodExpr.StreamingPayload, svc.PkgName)
-			svrRecvTypeRef = sd.Scope.GoFullTypeRef(e.MethodExpr.StreamingPayload, svc.PkgName)
-			svrPayload = buildRequestBodyType(e.StreamingBody, e.MethodExpr.StreamingPayload, e, true, sd)
-			if needInit(e.MethodExpr.StreamingPayload.Type) {
-				makeHTTPType(e.StreamingBody)
-				body := e.StreamingBody.Type
-				// generate constructor function to transform request body,
-				// into the method streaming payload type
-				var (
-					name       string
-					desc       string
-					serverArgs []*InitArgData
-					serverCode string
-					err        error
-				)
-				{
-					n := codegen.Goify(e.MethodExpr.Name, true)
-					p := codegen.Goify(svrPayload.Name, true)
-					// Raw payload object has type name prefixed with endpoint name. No need to
-					// prefix the type name again.
-					if strings.HasPrefix(p, n) {
-						name = fmt.Sprintf("New%s", p)
-					} else {
-						name = fmt.Sprintf("New%s%s", n, p)
-					}
-					desc = fmt.Sprintf("%s builds a %s service %s endpoint payload.", name, svc.Name, e.MethodExpr.Name)
-					if body != expr.Empty {
-						var (
-							ref    string
-							svcode string
-						)
-						{
-							ref = "body"
-							if expr.IsObject(body) {
-								ref = "&body"
-							}
-							if ut, ok := body.(expr.UserType); ok {
-								if val := ut.Attribute().Validation; val != nil {
-									httpctx := httpContext("", sd.Scope, true, true)
-									svcode = codegen.RecursiveValidationCode(ut.Attribute(), httpctx, true, "body")
-								}
-							}
-						}
-						serverArgs = []*InitArgData{{
-							Name:     "body",
-							Ref:      ref,
-							TypeName: sd.Scope.GoTypeName(e.StreamingBody),
-							TypeRef:  sd.Scope.GoTypeRef(e.StreamingBody),
-							Type:     e.StreamingBody.Type,
-							Required: true,
-							Example:  e.Body.Example(expr.Root.API.Random()),
-							Validate: svcode,
-						}}
-					}
-					if body != expr.Empty {
-						var helpers []*codegen.TransformFunctionData
-						httpctx := httpContext("", sd.Scope, true, true)
-						serverCode, helpers, err = marshal(e.StreamingBody, e.MethodExpr.StreamingPayload, "body", "v", httpctx, svcctx)
-						if err == nil {
-							sd.ServerTransformHelpers = codegen.AppendHelpers(sd.ServerTransformHelpers, helpers)
-						}
-					}
-					if err != nil {
-						fmt.Println(err.Error()) // TBD validate DSL so errors are not possible
-					}
-				}
-				svrPayload.Init = &InitData{
-					Name:           name,
-					Description:    desc,
-					ServerArgs:     serverArgs,
-					ReturnTypeName: svc.Scope.GoFullTypeName(e.MethodExpr.StreamingPayload, svc.PkgName),
-					ReturnTypeRef:  svc.Scope.GoFullTypeRef(e.MethodExpr.StreamingPayload, svc.PkgName),
-					ReturnIsStruct: expr.IsObject(e.MethodExpr.StreamingPayload.Type),
-					ReturnTypePkg:  svc.PkgName,
-					ServerCode:     serverCode,
-				}
-			}
-			cliPayload = buildRequestBodyType(e.StreamingBody, e.MethodExpr.StreamingPayload, e, false, sd)
-			if cliPayload != nil {
-				sd.ClientTypeNames[cliPayload.Name] = false
-				sd.ServerTypeNames[cliPayload.Name] = false
-			}
-			if e.MethodExpr.Stream == expr.ClientStreamKind {
-				svrSendDesc = fmt.Sprintf("%s streams instances of %q to the %q endpoint websocket connection and closes the connection.", md.ServerStream.SendName, svrSendTypeName, md.Name)
-				cliRecvDesc = fmt.Sprintf("%s stops sending messages to the %q endpoint websocket connection and reads instances of %q from the connection.", md.ClientStream.RecvName, md.Name, svrSendTypeName)
-			}
-			svrRecvDesc = fmt.Sprintf("%s reads instances of %q from the %q endpoint websocket connection.", md.ServerStream.RecvName, svrRecvTypeName, md.Name)
-			cliSendDesc = fmt.Sprintf("%s streams instances of %q to the %q endpoint websocket connection.", md.ClientStream.SendName, svrRecvTypeName, md.Name)
-		}
-	}
-	ed.ServerWebSocket = &WebSocketData{
-		VarName:      md.ServerStream.VarName,
-		Interface:    fmt.Sprintf("%s.%s", svc.PkgName, md.ServerStream.Interface),
-		Endpoint:     ed,
-		Payload:      svrPayload,
-		Response:     ed.Result.Responses[0],
-		PkgName:      svc.PkgName,
-		Type:         "server",
-		Kind:         md.ServerStream.Kind,
-		SendName:     md.ServerStream.SendName,
-		SendDesc:     svrSendDesc,
-		SendTypeName: svrSendTypeName,
-		SendTypeRef:  svrSendTypeRef,
-		RecvName:     md.ServerStream.RecvName,
-		RecvDesc:     svrRecvDesc,
-		RecvTypeName: svrRecvTypeName,
-		RecvTypeRef:  svrRecvTypeRef,
-		MustClose:    md.ServerStream.MustClose,
-	}
-	ed.ClientWebSocket = &WebSocketData{
-		VarName:      md.ClientStream.VarName,
-		Interface:    fmt.Sprintf("%s.%s", svc.PkgName, md.ClientStream.Interface),
-		Endpoint:     ed,
-		Payload:      cliPayload,
-		Response:     ed.Result.Responses[0],
-		PkgName:      svc.PkgName,
-		Type:         "client",
-		Kind:         md.ClientStream.Kind,
-		SendName:     md.ClientStream.SendName,
-		SendDesc:     cliSendDesc,
-		SendTypeName: svrRecvTypeName,
-		SendTypeRef:  svrRecvTypeRef,
-		RecvName:     md.ClientStream.RecvName,
-		RecvDesc:     cliRecvDesc,
-		RecvTypeName: svrSendTypeName,
-		RecvTypeRef:  svrSendTypeRef,
-		MustClose:    md.ClientStream.MustClose,
-	}
-}
-
 // buildRequestBodyType builds the TypeData for a request body. The data makes
 // it possible to generate a function on the client side that creates the body
 // from the service method payload.
@@ -2102,12 +1967,14 @@ func buildRequestBodyType(body, att *expr.AttributeExpr, e *expr.HTTPEndpointExp
 				sd.ClientTransformHelpers = codegen.AppendHelpers(sd.ClientTransformHelpers, helpers)
 			}
 			arg := InitArgData{
-				Name:     sourceVar,
-				Ref:      sourceVar,
-				TypeRef:  svc.Scope.GoFullTypeRef(att, svc.PkgName),
-				Type:     att.Type,
-				Validate: validateDef,
-				Example:  att.Example(expr.Root.API.Random()),
+				Ref: sourceVar,
+				AttributeData: &AttributeData{
+					VarName:  sourceVar,
+					TypeRef:  svc.Scope.GoFullTypeRef(att, svc.PkgName),
+					Type:     att.Type,
+					Validate: validateDef,
+					Example:  att.Example(expr.Root.API.Random()),
+				},
 			}
 			init = &InitData{
 				Name:                name,
@@ -2290,12 +2157,14 @@ func buildResponseBodyType(body, att *expr.AttributeExpr, e *expr.HTTPEndpointEx
 				tref = svc.ViewScope.GoFullTypeRef(att, svc.ViewsPkg)
 			}
 			arg := InitArgData{
-				Name:     sourceVar,
-				Ref:      ref,
-				TypeRef:  tref,
-				Type:     att.Type,
-				Validate: validateDef,
-				Example:  att.Example(expr.Root.API.Random()),
+				Ref: ref,
+				AttributeData: &AttributeData{
+					VarName:  sourceVar,
+					TypeRef:  tref,
+					Type:     att.Type,
+					Validate: validateDef,
+					Example:  att.Example(expr.Root.API.Random()),
+				},
 			}
 			init = &InitData{
 				Name:                name,
@@ -2341,25 +2210,29 @@ func extractPathParams(a *expr.MappedAttributeExpr, service *expr.AttributeExpr,
 			ft = service.Find(name).Type
 		}
 		params = append(params, &ParamData{
-			Name:           elem,
-			AttributeName:  name,
-			Description:    c.Description,
-			FieldName:      fieldName,
-			FieldPointer:   fptr,
-			FieldType:      ft,
-			VarName:        varn,
-			Required:       true,
-			Type:           c.Type,
-			TypeName:       scope.GoTypeName(c),
-			TypeRef:        scope.GoTypeRef(c),
-			Pointer:        false,
-			Slice:          arr != nil,
-			StringSlice:    arr != nil && arr.ElemType.Type.Kind() == expr.StringKind,
 			Map:            false,
 			MapStringSlice: false,
-			Validate:       codegen.RecursiveValidationCode(c, ctx, true, varn),
-			DefaultValue:   c.DefaultValue,
-			Example:        c.Example(expr.Root.API.Random()),
+			Element: &Element{
+				Name:          elem,
+				AttributeName: name,
+				Slice:         arr != nil,
+				StringSlice:   arr != nil && arr.ElemType.Type.Kind() == expr.StringKind,
+				AttributeData: &AttributeData{
+					Description:  c.Description,
+					FieldName:    fieldName,
+					FieldPointer: fptr,
+					FieldType:    ft,
+					VarName:      varn,
+					Required:     true,
+					Type:         c.Type,
+					TypeName:     scope.GoTypeName(c),
+					TypeRef:      scope.GoTypeRef(c),
+					Pointer:      false,
+					Validate:     codegen.RecursiveValidationCode(c, ctx, true, varn),
+					DefaultValue: c.DefaultValue,
+					Example:      c.Example(expr.Root.API.Random()),
+				},
+			},
 		})
 		return nil
 	})
@@ -2393,28 +2266,32 @@ func extractQueryParams(a *expr.MappedAttributeExpr, service *expr.AttributeExpr
 			ft = service.Find(name).Type
 		}
 		params = append(params, &ParamData{
-			Name:          elem,
-			AttributeName: name,
-			Description:   c.Description,
-			FieldName:     fieldName,
-			FieldPointer:  fptr,
-			FieldType:     ft,
-			VarName:       varn,
-			Required:      required,
-			Type:          c.Type,
-			TypeName:      scope.GoTypeName(c),
-			TypeRef:       typeRef,
-			Pointer:       pointer,
-			Slice:         arr != nil,
-			StringSlice:   arr != nil && arr.ElemType.Type.Kind() == expr.StringKind,
-			Map:           mp != nil,
+			Map: mp != nil,
 			MapStringSlice: mp != nil &&
 				mp.KeyType.Type.Kind() == expr.StringKind &&
 				mp.ElemType.Type.Kind() == expr.ArrayKind &&
 				expr.AsArray(mp.ElemType.Type).ElemType.Type.Kind() == expr.StringKind,
-			Validate:     codegen.RecursiveValidationCode(c, ctx, required, varn),
-			DefaultValue: c.DefaultValue,
-			Example:      c.Example(expr.Root.API.Random()),
+			Element: &Element{
+				Slice:         arr != nil,
+				StringSlice:   arr != nil && arr.ElemType.Type.Kind() == expr.StringKind,
+				Name:          elem,
+				AttributeName: name,
+				AttributeData: &AttributeData{
+					Description:  c.Description,
+					FieldName:    fieldName,
+					FieldPointer: fptr,
+					FieldType:    ft,
+					VarName:      varn,
+					Required:     required,
+					Type:         c.Type,
+					TypeName:     scope.GoTypeName(c),
+					TypeRef:      typeRef,
+					Pointer:      pointer,
+					Validate:     codegen.RecursiveValidationCode(c, ctx, required, varn),
+					DefaultValue: c.DefaultValue,
+					Example:      c.Example(expr.Root.API.Random()),
+				},
+			},
 		})
 		return nil
 	})
@@ -2455,30 +2332,106 @@ func extractHeaders(a *expr.MappedAttributeExpr, svcAtt *expr.AttributeExpr, svc
 			}
 		}
 		headers = append(headers, &HeaderData{
-			Name:          elem,
-			AttributeName: name,
-			Description:   hattr.Description,
 			CanonicalName: http.CanonicalHeaderKey(elem),
-			FieldName:     fieldName,
-			FieldPointer:  fptr,
-			FieldType:     ft,
-			VarName:       varn,
-			TypeName:      scope.GoTypeName(hattr),
-			TypeRef:       typeRef,
-			Required:      required,
-			Pointer:       pointer,
-			Slice:         arr != nil,
-			StringSlice:   arr != nil && arr.ElemType.Type.Kind() == expr.StringKind,
-			Type:          hattr.Type,
-			Validate:      codegen.RecursiveValidationCode(hattr, svcCtx, required, varn),
-			DefaultValue:  hattr.DefaultValue,
-			Example:       hattr.Example(expr.Root.API.Random()),
+			Element: &Element{
+				Name:          elem,
+				Slice:         arr != nil,
+				StringSlice:   arr != nil && arr.ElemType.Type.Kind() == expr.StringKind,
+				AttributeName: name,
+				AttributeData: &AttributeData{
+					Description:  hattr.Description,
+					FieldName:    fieldName,
+					FieldPointer: fptr,
+					FieldType:    ft,
+					VarName:      varn,
+					TypeName:     scope.GoTypeName(hattr),
+					TypeRef:      typeRef,
+					Required:     required,
+					Pointer:      pointer,
+					Type:         hattr.Type,
+					Validate:     codegen.RecursiveValidationCode(hattr, svcCtx, required, varn),
+					DefaultValue: hattr.DefaultValue,
+					Example:      hattr.Example(expr.Root.API.Random()),
+				},
+			},
 		})
 		return nil
 	})
 	return headers
 }
 
+func extractCookies(a *expr.MappedAttributeExpr, svcAtt *expr.AttributeExpr, svcCtx *codegen.AttributeContext, scope *codegen.NameScope) []*CookieData {
+	var cookies []*CookieData
+	codegen.WalkMappedAttr(a, func(name, elem string, required bool, _ *expr.AttributeExpr) error {
+		var hattr *expr.AttributeExpr
+		{
+			if hattr = svcAtt.Find(name); hattr == nil {
+				hattr = svcAtt
+			}
+			hattr = expr.DupAtt(hattr)
+			makeHTTPType(hattr)
+		}
+		var (
+			varn    = scope.Name(codegen.Goify(name, false))
+			typeRef = scope.GoTypeRef(hattr)
+			ft      = svcAtt.Type
+
+			fieldName string
+			pointer   bool
+			fptr      bool
+		)
+		{
+			pointer = a.IsPrimitivePointer(name, true)
+			if expr.IsObject(svcAtt.Type) {
+				fieldName = codegen.Goify(name, true)
+				fptr = svcCtx.IsPrimitivePointer(name, svcAtt)
+				ft = svcAtt.Find(name).Type
+			}
+			if pointer {
+				typeRef = "*" + typeRef
+			}
+		}
+		c := &CookieData{
+			Element: &Element{
+				Name:          elem,
+				AttributeName: name,
+				AttributeData: &AttributeData{
+					Description:  hattr.Description,
+					FieldName:    fieldName,
+					FieldPointer: fptr,
+					FieldType:    ft,
+					VarName:      varn,
+					TypeName:     scope.GoTypeName(hattr),
+					TypeRef:      typeRef,
+					Required:     required,
+					Pointer:      pointer,
+					Type:         hattr.Type,
+					Validate:     codegen.RecursiveValidationCode(hattr, svcCtx, required, varn),
+					DefaultValue: hattr.DefaultValue,
+					Example:      hattr.Example(expr.Root.API.Random()),
+				},
+			},
+		}
+		for n, v := range a.Meta {
+			switch n {
+			case "cookie:max-age":
+				c.MaxAge = v[0]
+			case "cookie:path":
+				c.Path = v[0]
+			case "cookie:domain":
+				c.Domain = v[0]
+			case "cookie:secure":
+				c.Secure = v[0] == "Secure"
+			case "cookie:http-only":
+				c.HTTPOnly = v[0] == "HttpOnly"
+			}
+		}
+		cookies = append(cookies, c)
+		return nil
+	})
+	return cookies
+}
+
 // collectUserTypes traverses the given data type recursively and calls back the
 // given function for each attribute using a user type.
 func collectUserTypes(dt expr.DataType, cb func(expr.UserType), seen ...map[string]struct{}) {
@@ -2698,15 +2651,15 @@ const (
 	{{- range $i, $arg := .Args }}
 		{{- $typ := (index $.PathParams $i).Attribute.Type }}
 		{{- if eq $typ.Name "array" }}
-	{{ .Name }}Slice := make([]string, len({{ .Name }}))
-	for i, v := range {{ .Name }} {
-		{{ .Name }}Slice[i] = {{ template "slice_conversion" $typ.ElemType.Type.Name }}
+	{{ .VarName }}Slice := make([]string, len({{ .VarName }}))
+	for i, v := range {{ .VarName }} {
+		{{ .VarName }}Slice[i] = {{ template "slice_conversion" $typ.ElemType.Type.Name }}
 	}
 		{{- end }}
 	{{- end }}
 	return fmt.Sprintf("{{ .PathFormat }}", {{ range $i, $arg := .Args }}
-	{{- if eq (index $.PathParams $i).Attribute.Type.Name "array" }}strings.Join({{ .Name }}Slice, ",")
-	{{- else }}{{ .Name }}
+	{{- if eq (index $.PathParams $i).Attribute.Type.Name "array" }}strings.Join({{ .VarName }}Slice, ",")
+	{{- else }}{{ .VarName }}
 	{{- end }}, {{ end }})
 {{- else }}
 	return "{{ .PathFormat }}"
@@ -2732,7 +2685,7 @@ const (
 {{- if or .Args .RequestStruct }}
 	var (
 	{{- range .Args }}
-		{{ .Name }} {{ .TypeRef }}
+		{{ .VarName }} {{ .TypeRef }}
 	{{- end }}
 	{{- if .RequestStruct }}
 		body io.Reader
@@ -2759,9 +2712,9 @@ const (
 		if p{{ if $.HasFields }}.{{ .FieldName }}{{ end }} != nil {
 		{{- end }}
 			{{- if (isAliased .FieldType) }}
-			{{ .Name }} = {{ goTypeRef .Type $.ServiceName }}({{ if .Pointer }}*{{ end }}p{{ if $.HasFields }}.{{ .FieldName }}{{ end }})
+			{{ .VarName }} = {{ goTypeRef .Type $.ServiceName }}({{ if .Pointer }}*{{ end }}p{{ if $.HasFields }}.{{ .FieldName }}{{ end }})
 			{{- else }}
-			{{ .Name }} = {{ if .Pointer }}*{{ end }}p{{ if $.HasFields }}.{{ .FieldName }}{{ end }}
+			{{ .VarName }} = {{ if .Pointer }}*{{ end }}p{{ if $.HasFields }}.{{ .FieldName }}{{ end }}
 			{{- end }}
 		{{- if .Pointer }}
 		}
diff --git a/http/codegen/websocket.go b/http/codegen/websocket.go
index 39e5ffb0..8b5c71fc 100644
--- a/http/codegen/websocket.go
+++ b/http/codegen/websocket.go
@@ -3,11 +3,218 @@ package codegen
 import (
 	"fmt"
 	"path/filepath"
+	"strings"
 
 	"goa.design/goa/v3/codegen"
 	"goa.design/goa/v3/expr"
 )
 
+type (
+	// WebSocketData contains the data needed to render struct type that
+	// implements the server and client stream interfaces.
+	WebSocketData struct {
+		// VarName is the name of the struct.
+		VarName string
+		// Type is type of the stream (server or client).
+		Type string
+		// Interface is the fully qualified name of the interface that
+		// the struct implements.
+		Interface string
+		// Endpoint is endpoint data that defines streaming
+		// payload/result.
+		Endpoint *EndpointData
+		// Payload is the streaming payload type sent via the stream.
+		Payload *TypeData
+		// Response is the successful response data for the streaming
+		// endpoint.
+		Response *ResponseData
+		// SendName is the name of the send function.
+		SendName string
+		// SendDesc is the description for the send function.
+		SendDesc string
+		// SendTypeName is the fully qualified type name sent through
+		// the stream.
+		SendTypeName string
+		// SendTypeRef is the fully qualified type ref sent through the
+		// stream.
+		SendTypeRef string
+		// RecvName is the name of the receive function.
+		RecvName string
+		// RecvDesc is the description for the recv function.
+		RecvDesc string
+		// RecvTypeName is the fully qualified type name received from
+		// the stream.
+		RecvTypeName string
+		// RecvTypeRef is the fully qualified type ref received from the
+		// stream.
+		RecvTypeRef string
+		// MustClose indicates whether to generate the Close() function
+		// for the stream.
+		MustClose bool
+		// PkgName is the service package name.
+		PkgName string
+		// Kind is the kind of the stream (payload, result or
+		// bidirectional).
+		Kind expr.StreamKind
+	}
+)
+
+// initWebSocketData initializes the WebSocket related data in ed.
+func initWebSocketData(ed *EndpointData, e *expr.HTTPEndpointExpr, sd *ServiceData) {
+	var (
+		svrSendTypeName string
+		svrSendTypeRef  string
+		svrRecvTypeName string
+		svrRecvTypeRef  string
+		svrSendDesc     string
+		svrRecvDesc     string
+		svrPayload      *TypeData
+		cliSendDesc     string
+		cliRecvDesc     string
+		cliPayload      *TypeData
+
+		md     = ed.Method
+		svc    = sd.Service
+		svcctx = serviceContext(sd.Service.PkgName, sd.Service.Scope)
+	)
+	{
+		svrSendTypeName = ed.Result.Name
+		svrSendTypeRef = ed.Result.Ref
+		svrSendDesc = fmt.Sprintf("%s streams instances of %q to the %q endpoint websocket connection.", md.ServerStream.SendName, svrSendTypeName, md.Name)
+		cliRecvDesc = fmt.Sprintf("%s reads instances of %q from the %q endpoint websocket connection.", md.ClientStream.RecvName, svrSendTypeName, md.Name)
+		if e.MethodExpr.Stream == expr.ClientStreamKind || e.MethodExpr.Stream == expr.BidirectionalStreamKind {
+			svrRecvTypeName = sd.Scope.GoFullTypeName(e.MethodExpr.StreamingPayload, svc.PkgName)
+			svrRecvTypeRef = sd.Scope.GoFullTypeRef(e.MethodExpr.StreamingPayload, svc.PkgName)
+			svrPayload = buildRequestBodyType(e.StreamingBody, e.MethodExpr.StreamingPayload, e, true, sd)
+			if needInit(e.MethodExpr.StreamingPayload.Type) {
+				makeHTTPType(e.StreamingBody)
+				body := e.StreamingBody.Type
+				// generate constructor function to transform request body,
+				// into the method streaming payload type
+				var (
+					name       string
+					desc       string
+					serverArgs []*InitArgData
+					serverCode string
+					err        error
+				)
+				{
+					n := codegen.Goify(e.MethodExpr.Name, true)
+					p := codegen.Goify(svrPayload.Name, true)
+					// Raw payload object has type name prefixed with endpoint name. No need to
+					// prefix the type name again.
+					if strings.HasPrefix(p, n) {
+						name = fmt.Sprintf("New%s", p)
+					} else {
+						name = fmt.Sprintf("New%s%s", n, p)
+					}
+					desc = fmt.Sprintf("%s builds a %s service %s endpoint payload.", name, svc.Name, e.MethodExpr.Name)
+					if body != expr.Empty {
+						var (
+							ref    string
+							svcode string
+						)
+						{
+							ref = "body"
+							if expr.IsObject(body) {
+								ref = "&body"
+							}
+							if ut, ok := body.(expr.UserType); ok {
+								if val := ut.Attribute().Validation; val != nil {
+									httpctx := httpContext("", sd.Scope, true, true)
+									svcode = codegen.RecursiveValidationCode(ut.Attribute(), httpctx, true, "body")
+								}
+							}
+						}
+						serverArgs = []*InitArgData{{
+							Ref: ref,
+							AttributeData: &AttributeData{
+								VarName:  "body",
+								TypeName: sd.Scope.GoTypeName(e.StreamingBody),
+								TypeRef:  sd.Scope.GoTypeRef(e.StreamingBody),
+								Type:     e.StreamingBody.Type,
+								Required: true,
+								Example:  e.Body.Example(expr.Root.API.Random()),
+								Validate: svcode,
+							},
+						}}
+					}
+					if body != expr.Empty {
+						var helpers []*codegen.TransformFunctionData
+						httpctx := httpContext("", sd.Scope, true, true)
+						serverCode, helpers, err = marshal(e.StreamingBody, e.MethodExpr.StreamingPayload, "body", "v", httpctx, svcctx)
+						if err == nil {
+							sd.ServerTransformHelpers = codegen.AppendHelpers(sd.ServerTransformHelpers, helpers)
+						}
+					}
+					if err != nil {
+						fmt.Println(err.Error()) // TBD validate DSL so errors are not possible
+					}
+				}
+				svrPayload.Init = &InitData{
+					Name:           name,
+					Description:    desc,
+					ServerArgs:     serverArgs,
+					ReturnTypeName: svc.Scope.GoFullTypeName(e.MethodExpr.StreamingPayload, svc.PkgName),
+					ReturnTypeRef:  svc.Scope.GoFullTypeRef(e.MethodExpr.StreamingPayload, svc.PkgName),
+					ReturnIsStruct: expr.IsObject(e.MethodExpr.StreamingPayload.Type),
+					ReturnTypePkg:  svc.PkgName,
+					ServerCode:     serverCode,
+				}
+			}
+			cliPayload = buildRequestBodyType(e.StreamingBody, e.MethodExpr.StreamingPayload, e, false, sd)
+			if cliPayload != nil {
+				sd.ClientTypeNames[cliPayload.Name] = false
+				sd.ServerTypeNames[cliPayload.Name] = false
+			}
+			if e.MethodExpr.Stream == expr.ClientStreamKind {
+				svrSendDesc = fmt.Sprintf("%s streams instances of %q to the %q endpoint websocket connection and closes the connection.", md.ServerStream.SendName, svrSendTypeName, md.Name)
+				cliRecvDesc = fmt.Sprintf("%s stops sending messages to the %q endpoint websocket connection and reads instances of %q from the connection.", md.ClientStream.RecvName, md.Name, svrSendTypeName)
+			}
+			svrRecvDesc = fmt.Sprintf("%s reads instances of %q from the %q endpoint websocket connection.", md.ServerStream.RecvName, svrRecvTypeName, md.Name)
+			cliSendDesc = fmt.Sprintf("%s streams instances of %q to the %q endpoint websocket connection.", md.ClientStream.SendName, svrRecvTypeName, md.Name)
+		}
+	}
+	ed.ServerWebSocket = &WebSocketData{
+		VarName:      md.ServerStream.VarName,
+		Interface:    fmt.Sprintf("%s.%s", svc.PkgName, md.ServerStream.Interface),
+		Endpoint:     ed,
+		Payload:      svrPayload,
+		Response:     ed.Result.Responses[0],
+		PkgName:      svc.PkgName,
+		Type:         "server",
+		Kind:         md.ServerStream.Kind,
+		SendName:     md.ServerStream.SendName,
+		SendDesc:     svrSendDesc,
+		SendTypeName: svrSendTypeName,
+		SendTypeRef:  svrSendTypeRef,
+		RecvName:     md.ServerStream.RecvName,
+		RecvDesc:     svrRecvDesc,
+		RecvTypeName: svrRecvTypeName,
+		RecvTypeRef:  svrRecvTypeRef,
+		MustClose:    md.ServerStream.MustClose,
+	}
+	ed.ClientWebSocket = &WebSocketData{
+		VarName:      md.ClientStream.VarName,
+		Interface:    fmt.Sprintf("%s.%s", svc.PkgName, md.ClientStream.Interface),
+		Endpoint:     ed,
+		Payload:      cliPayload,
+		Response:     ed.Result.Responses[0],
+		PkgName:      svc.PkgName,
+		Type:         "client",
+		Kind:         md.ClientStream.Kind,
+		SendName:     md.ClientStream.SendName,
+		SendDesc:     cliSendDesc,
+		SendTypeName: svrRecvTypeName,
+		SendTypeRef:  svrRecvTypeRef,
+		RecvName:     md.ClientStream.RecvName,
+		RecvDesc:     cliRecvDesc,
+		RecvTypeName: svrSendTypeName,
+		RecvTypeRef:  svrSendTypeRef,
+		MustClose:    md.ClientStream.MustClose,
+	}
+}
+
 // websocketServerFile returns the file implementing the WebSocket server
 // streaming implementation if any.
 func websocketServerFile(genpkg string, svc *expr.HTTPServiceExpr) *codegen.File {


Test patch:
diff --git a/expr/api_test.go b/expr/api_test.go
index 52efcd9c..47b61d26 100644
--- a/expr/api_test.go
+++ b/expr/api_test.go
@@ -11,36 +11,27 @@ func TestAPIExprSchemes(t *testing.T) {
 	}{
 		"default scheme": {
 			expr: APIExpr{
-				Servers: []*ServerExpr{&ServerExpr{}},
+				Servers: []*ServerExpr{{}},
 			},
 			expected: nil,
 		},
 		"single scheme": {
 			expr: APIExpr{
-				Servers: []*ServerExpr{
-					&ServerExpr{
-						Hosts: []*HostExpr{
-							{URIs: []URIExpr{"http://example.com"}},
-						},
-					},
+				Servers: []*ServerExpr{{
+					Hosts: []*HostExpr{
+						{URIs: []URIExpr{"http://example.com"}},
+					}},
 				},
 			},
 			expected: []string{"http"},
 		},
 		"multiple schemes": {
 			expr: APIExpr{
-				Servers: []*ServerExpr{
-					&ServerExpr{
-						Hosts: []*HostExpr{
-							{URIs: []URIExpr{"http://example.com"}},
-						},
-					},
-					&ServerExpr{
-						Hosts: []*HostExpr{
-							{URIs: []URIExpr{"https://example.net"}},
-						},
-					},
-				},
+				Servers: []*ServerExpr{{
+					Hosts: []*HostExpr{{URIs: []URIExpr{"http://example.com"}}},
+				}, {
+					Hosts: []*HostExpr{{URIs: []URIExpr{"https://example.net"}}},
+				}},
 			},
 			expected: []string{"http", "https"},
 		},
diff --git a/expr/http_endpoint_test.go b/expr/http_endpoint_test.go
index 0b157237..46596164 100644
--- a/expr/http_endpoint_test.go
+++ b/expr/http_endpoint_test.go
@@ -144,7 +144,7 @@ func TestHTTPEndpointValidation(t *testing.T) {
 		"endpoint-has-skip-response-encode-and-result-streaming": {
 			DSL: testdata.EndpointHasSkipResponseEncodeAndResultStreaming,
 			Error: `service "Service" HTTP endpoint "Method": Endpoint cannot use SkipResponseBodyEncodeDecode when method defines a StreamingResult.
-service "Service" HTTP endpoint "Method": HTTP endpoint response body must be empty when using SkipResponseBodyEncodeDecode. Make sure to define Headers as needed.`,
+service "Service" HTTP endpoint "Method": HTTP endpoint response body must be empty when using SkipResponseBodyEncodeDecode. Make sure to define headers and cookies as needed.`,
 		},
 		"endpoint-has-skip-encode-and-grpc": {
 			DSL:   testdata.EndpointHasSkipEncodeAndGRPC,