From d0c85b6dd964164b6d92103ad65b5cd859197de2 Mon Sep 17 00:00:00 2001
From: Rider Linden <none@none>
Date: Fri, 10 Apr 2015 17:23:58 -0700
Subject: [PATCH] Adding support for DELETE, PATCH and COPY

---
 indra/llcorehttp/_httpoprequest.cpp | 68 ++++++++++++++++++---
 indra/llcorehttp/_httpoprequest.h   | 26 +++++++-
 indra/llcorehttp/httprequest.cpp    | 94 +++++++++++++++++++++++++++++
 indra/llcorehttp/httprequest.h      | 63 ++++++++++++++++++-
 indra/llmessage/llcorehttputil.cpp  | 40 ++++++++++++
 indra/llmessage/llcorehttputil.h    | 35 +++++++++++
 6 files changed, 315 insertions(+), 11 deletions(-)

diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp
index 7c2309b31df..b1b05dc285f 100755
--- a/indra/llcorehttp/_httpoprequest.cpp
+++ b/indra/llcorehttp/_httpoprequest.cpp
@@ -357,6 +357,46 @@ HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id,
 }
 
 
+HttpStatus HttpOpRequest::setupDelete(HttpRequest::policy_t policy_id,
+    HttpRequest::priority_t priority,
+    const std::string & url,
+    HttpOptions * options,
+    HttpHeaders * headers)
+{
+    setupCommon(policy_id, priority, url, NULL, options, headers);
+    mReqMethod = HOR_DELETE;
+
+    return HttpStatus();
+}
+
+
+HttpStatus HttpOpRequest::setupPatch(HttpRequest::policy_t policy_id,
+    HttpRequest::priority_t priority,
+    const std::string & url,
+    BufferArray * body,
+    HttpOptions * options,
+    HttpHeaders * headers)
+{
+    setupCommon(policy_id, priority, url, body, options, headers);
+    mReqMethod = HOR_PATCH;
+
+    return HttpStatus();
+}
+
+
+HttpStatus HttpOpRequest::setupCopy(HttpRequest::policy_t policy_id,
+    HttpRequest::priority_t priority,
+    const std::string & url,
+    HttpOptions * options,
+    HttpHeaders * headers)
+{
+    setupCommon(policy_id, priority, url, NULL, options, headers);
+    mReqMethod = HOR_COPY;
+
+    return HttpStatus();
+}
+
+
 void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,
 								HttpRequest::priority_t priority,
 								const std::string & url,
@@ -549,8 +589,6 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 	case HOR_GET:
 		code = curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1);
 		check_curl_easy_code(code, CURLOPT_HTTPGET);
-		mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
-		mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
 		break;
 		
 	case HOR_POST:
@@ -569,12 +607,14 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 			code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size);
 			check_curl_easy_code(code, CURLOPT_POSTFIELDSIZE);
 			mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
-			mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
-			mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
 		}
 		break;
 		
-	case HOR_PUT:
+    case HOR_PATCH:
+        code = curl_easy_setopt(mCurlHandle, CURLOPT_CUSTOMREQUEST, "PATCH");
+        check_curl_easy_code(code, CURLOPT_CUSTOMREQUEST);
+        // fall through.  The rest is the same as PUT
+    case HOR_PUT:
 		{
 			code = curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1);
 			check_curl_easy_code(code, CURLOPT_UPLOAD);
@@ -588,12 +628,19 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 			code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL);
 			check_curl_easy_code(code, CURLOPT_POSTFIELDS);
 			mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
-			// *TODO: Should this be 'Keep-Alive' ?
-			mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
-			mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
 		}
 		break;
 		
+    case HOR_DELETE:
+        code = curl_easy_setopt(mCurlHandle, CURLOPT_CUSTOMREQUEST, "DELETE");
+        check_curl_easy_code(code, CURLOPT_CUSTOMREQUEST);
+        break;
+
+    case HOR_COPY:
+        code = curl_easy_setopt(mCurlHandle, CURLOPT_CUSTOMREQUEST, "COPY");
+        check_curl_easy_code(code, CURLOPT_CUSTOMREQUEST);
+        break;
+
 	default:
 		LL_ERRS(LOG_CORE) << "Invalid HTTP method in request:  "
 						  << int(mReqMethod)  << ".  Can't recover."
@@ -601,6 +648,11 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 		break;
 	}
 
+
+    // *TODO: Should this be 'Keep-Alive' ?
+    mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
+    mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
+
 	// Tracing
 	if (mTracing >= HTTP_TRACE_CURL_HEADERS)
 	{
diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h
index e71d1d1edf2..ca40898a81d 100755
--- a/indra/llcorehttp/_httpoprequest.h
+++ b/indra/llcorehttp/_httpoprequest.h
@@ -80,7 +80,10 @@ class HttpOpRequest : public HttpOperation
 	{
 		HOR_GET,
 		HOR_POST,
-		HOR_PUT
+		HOR_PUT,
+        HOR_DELETE,
+        HOR_PATCH,
+        HOR_COPY
 	};
 	
 	virtual void stageFromRequest(HttpService *);
@@ -126,7 +129,26 @@ class HttpOpRequest : public HttpOperation
 						HttpOptions * options,
 						HttpHeaders * headers);
 
-	// Internal method used to setup the libcurl options for a request.
+    HttpStatus setupDelete(HttpRequest::policy_t policy_id,
+                        HttpRequest::priority_t priority,
+                        const std::string & url,
+                        HttpOptions * options,
+                        HttpHeaders * headers);
+
+    HttpStatus setupPatch(HttpRequest::policy_t policy_id,
+                        HttpRequest::priority_t priority,
+                        const std::string & url,
+                        BufferArray * body,
+                        HttpOptions * options,
+                        HttpHeaders * headers);
+
+    HttpStatus setupCopy(HttpRequest::policy_t policy_id,
+                        HttpRequest::priority_t priority,
+                        const std::string & url,
+                        HttpOptions * options,
+                        HttpHeaders * headers);
+
+    // Internal method used to setup the libcurl options for a request.
 	// Does all the libcurl handle setup in one place.
 	//
 	// Threading:  called by worker thread
diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp
index df8502b947b..d4c60a6f14a 100755
--- a/indra/llcorehttp/httprequest.cpp
+++ b/indra/llcorehttp/httprequest.cpp
@@ -325,6 +325,100 @@ HttpHandle HttpRequest::requestPut(policy_t policy_id,
 	return handle;
 }
 
+HttpHandle HttpRequest::requestDelete(policy_t policy_id,
+    priority_t priority,
+    const std::string & url,
+    HttpOptions * options,
+    HttpHeaders * headers,
+    HttpHandler * user_handler)
+{
+    HttpStatus status;
+    HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
+
+    HttpOpRequest * op = new HttpOpRequest();
+    if (!(status = op->setupDelete(policy_id, priority, url, options, headers)))
+    {
+        op->release();
+        mLastReqStatus = status;
+        return handle;
+    }
+    op->setReplyPath(mReplyQueue, user_handler);
+    if (!(status = mRequestQueue->addOp(op)))			// transfers refcount
+    {
+        op->release();
+        mLastReqStatus = status;
+        return handle;
+    }
+
+    mLastReqStatus = status;
+    handle = static_cast<HttpHandle>(op);
+
+    return handle;
+}
+
+HttpHandle HttpRequest::requestPatch(policy_t policy_id,
+    priority_t priority,
+    const std::string & url,
+    BufferArray * body,
+    HttpOptions * options,
+    HttpHeaders * headers,
+    HttpHandler * user_handler)
+{
+    HttpStatus status;
+    HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
+
+    HttpOpRequest * op = new HttpOpRequest();
+    if (!(status = op->setupPatch(policy_id, priority, url, body, options, headers)))
+    {
+        op->release();
+        mLastReqStatus = status;
+        return handle;
+    }
+    op->setReplyPath(mReplyQueue, user_handler);
+    if (!(status = mRequestQueue->addOp(op)))			// transfers refcount
+    {
+        op->release();
+        mLastReqStatus = status;
+        return handle;
+    }
+
+    mLastReqStatus = status;
+    handle = static_cast<HttpHandle>(op);
+
+    return handle;
+}
+
+HttpHandle HttpRequest::requestCopy(policy_t policy_id,
+    priority_t priority,
+    const std::string & url,
+    HttpOptions * options,
+    HttpHeaders * headers,
+    HttpHandler * user_handler)
+{
+    HttpStatus status;
+    HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
+
+    HttpOpRequest * op = new HttpOpRequest();
+    if (!(status = op->setupCopy(policy_id, priority, url, options, headers)))
+    {
+        op->release();
+        mLastReqStatus = status;
+        return handle;
+    }
+    op->setReplyPath(mReplyQueue, user_handler);
+    if (!(status = mRequestQueue->addOp(op)))			// transfers refcount
+    {
+        op->release();
+        mLastReqStatus = status;
+        return handle;
+    }
+
+    mLastReqStatus = status;
+    handle = static_cast<HttpHandle>(op);
+
+    return handle;
+}
+
 
 HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler)
 {
diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h
index 6688f06eb5e..e87a8b691a7 100755
--- a/indra/llcorehttp/httprequest.h
+++ b/indra/llcorehttp/httprequest.h
@@ -478,7 +478,68 @@ class HttpRequest
 						  HttpHandler * handler);
 
 
-	/// Queue a NoOp request.
+    /// Queue a full HTTP PUT.  Query arguments and body may
+    /// be provided.  Caller is responsible for escaping and
+    /// encoding and communicating the content types.
+    ///
+    /// @param	policy_id		@see requestGet()
+    /// @param	priority		"
+    /// @param	url				"
+    /// @param	options			@see requestGet()K(optional)
+    /// @param	headers			"
+    /// @param	handler			"
+    /// @return					"
+    ///
+    HttpHandle requestDelete(policy_t policy_id,
+            priority_t priority,
+            const std::string & url,
+            HttpOptions * options,
+            HttpHeaders * headers,
+            HttpHandler * user_handler);
+
+    /// Queue a full HTTP PUT.  Query arguments and body may
+    /// be provided.  Caller is responsible for escaping and
+    /// encoding and communicating the content types.
+    ///
+    /// @param	policy_id		@see requestGet()
+    /// @param	priority		"
+    /// @param	url				"
+    /// @param	body			Byte stream to be sent as the body.  No
+    ///							further encoding or escaping will be done
+    ///							to the content.
+    /// @param	options			@see requestGet()K(optional)
+    /// @param	headers			"
+    /// @param	handler			"
+    /// @return					"
+    ///
+    HttpHandle requestPatch(policy_t policy_id,
+            priority_t priority,
+            const std::string & url,
+            BufferArray * body,
+            HttpOptions * options,
+            HttpHeaders * headers,
+            HttpHandler * user_handler);
+
+    /// Queue a full HTTP PUT.  Query arguments and body may
+    /// be provided.  Caller is responsible for escaping and
+    /// encoding and communicating the content types.
+    ///
+    /// @param	policy_id		@see requestGet()
+    /// @param	priority		"
+    /// @param	url				"
+    /// @param	options			@see requestGet()K(optional)
+    /// @param	headers			"
+    /// @param	handler			"
+    /// @return					"
+    ///
+    HttpHandle requestCopy(policy_t policy_id,
+            priority_t priority,
+            const std::string & url,
+            HttpOptions * options,
+            HttpHeaders * headers,
+            HttpHandler * user_handler);
+       
+    /// Queue a NoOp request.
 	/// The request is queued and serviced by the working thread which
 	/// immediately processes it and returns the request to the reply
 	/// queue.
diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp
index a32c4cad22f..2d6cca214c8 100644
--- a/indra/llmessage/llcorehttputil.cpp
+++ b/indra/llmessage/llcorehttputil.cpp
@@ -142,6 +142,46 @@ HttpHandle requestPutWithLLSD(HttpRequest::ptr_t & request,
         url, body, options.get(), headers.get(), handler);
 }
 
+HttpHandle requestPatchWithLLSD(HttpRequest * request,
+    HttpRequest::policy_t policy_id,
+    HttpRequest::priority_t priority,
+    const std::string & url,
+    const LLSD & body,
+    HttpOptions * options,
+    HttpHeaders * headers,
+    HttpHandler * handler)
+{
+    HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
+
+    BufferArray * ba = new BufferArray();
+    BufferArrayStream bas(ba);
+    LLSDSerialize::toXML(body, bas);
+
+    handle = request->requestPatch(policy_id,
+        priority,
+        url,
+        ba,
+        options,
+        headers,
+        handler);
+    ba->release();
+    return handle;
+}
+
+HttpHandle requestPatchWithLLSD(HttpRequest::ptr_t & request,
+    HttpRequest::policy_t policy_id,
+    HttpRequest::priority_t priority,
+    const std::string & url,
+    const LLSD & body,
+    HttpOptions::ptr_t & options,
+    HttpHeaders::ptr_t & headers,
+    HttpHandler * handler)
+{
+    return requestPatchWithLLSD(request.get(), policy_id, priority,
+        url, body, options.get(), headers.get(), handler);
+}
+
+
 std::string responseToString(LLCore::HttpResponse * response)
 {
     static const std::string empty("[Empty]");
diff --git a/indra/llmessage/llcorehttputil.h b/indra/llmessage/llcorehttputil.h
index 471710f61b3..6fcf03b95c0 100644
--- a/indra/llmessage/llcorehttputil.h
+++ b/indra/llmessage/llcorehttputil.h
@@ -155,6 +155,41 @@ LLCore::HttpHandle requestPutWithLLSD(LLCore::HttpRequest::ptr_t & request,
 	LLCore::HttpHeaders::ptr_t & headers,
 	LLCore::HttpHandler * handler);
 
+/// Issue a standard HttpRequest::requestPatch() call but using
+/// and LLSD object as the request body.  Conventions are the
+/// same as with that method.  Caller is expected to provide
+/// an HttpHeaders object with a correct 'Content-Type:' header.
+/// One will not be provided by this call.
+///
+/// @return				If request is successfully issued, the
+///						HttpHandle representing the request.
+///						On error, LLCORE_HTTP_HANDLE_INVALID
+///						is returned and caller can fetch detailed
+///						status with the getStatus() method on the
+///						request object.  In case of error, no
+///						request is queued and caller may need to
+///						perform additional cleanup such as freeing
+///						a now-useless HttpHandler object.
+///
+LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest * request,
+    LLCore::HttpRequest::policy_t policy_id,
+    LLCore::HttpRequest::priority_t priority,
+    const std::string & url,
+    const LLSD & body,
+    LLCore::HttpOptions * options,
+    LLCore::HttpHeaders * headers,
+    LLCore::HttpHandler * handler);
+
+LLCore::HttpHandle requestPatchWithLLSD(LLCore::HttpRequest::ptr_t & request,
+    LLCore::HttpRequest::policy_t policy_id,
+    LLCore::HttpRequest::priority_t priority,
+    const std::string & url,
+    const LLSD & body,
+    LLCore::HttpOptions::ptr_t & options,
+    LLCore::HttpHeaders::ptr_t & headers,
+    LLCore::HttpHandler * handler);
+
+
 /// The HttpCoroHandler is a specialization of the LLCore::HttpHandler for 
 /// interacting with coroutines. When the request is completed the response 
 /// will be posted onto the supplied Event Pump.
-- 
GitLab