Fossil SCM
Fix http_build_header() so /my%20repo stops sending 'POST /my repo', etc. Percent encode everything that RFC 3986 says MUST NOT be in a path, giving eg send POST /my%20repo. Ported from libfossil.
Commit
c4dec3a6ec5b8ed82b63a20202b002e21c3d61f427447ba07a8ed7cf4d9904c0
Parent
4df4f6c1dfa9e6a…
1 file changed
+25
-6
+25
-6
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -127,10 +127,29 @@ | ||
| 127 | 127 | blob_appendf(pLogin, "login %F %b %b", zLogin, &nonce, &sig); |
| 128 | 128 | blob_reset(&pw); |
| 129 | 129 | blob_reset(&sig); |
| 130 | 130 | blob_reset(&nonce); |
| 131 | 131 | } |
| 132 | + | |
| 133 | +/* | |
| 134 | +** Append zPath to pBlob as the target of an HTTP request line (RFC 9112 | |
| 135 | +** section 3.2). zPath is the decoded path from the URL, so it is | |
| 136 | +** percent-encoded again here: each byte outside ASCII and each | |
| 137 | +** character that RFC 3986 section 3.3 does not allow in a path is | |
| 138 | +** written as %XX. '+' is encoded too, because the server decodes it | |
| 139 | +** as a space. | |
| 140 | +*/ | |
| 141 | +static void http_append_request_target(Blob *pBlob, const char *zPath){ | |
| 142 | + for(; *zPath; zPath++){ | |
| 143 | + unsigned char c = (unsigned char)*zPath; | |
| 144 | + if( c<=0x20 || c>=0x7f || strchr("\"#%+<>?[\\]^`{|}", c)!=0 ){ | |
| 145 | + blob_appendf(pBlob, "%%%02X", c); | |
| 146 | + }else{ | |
| 147 | + blob_append_char(pBlob, (char)c); | |
| 148 | + } | |
| 149 | + } | |
| 150 | +} | |
| 132 | 151 | |
| 133 | 152 | /* |
| 134 | 153 | ** Construct an appropriate HTTP request header. Write the header |
| 135 | 154 | ** into pHdr. This routine initializes the pHdr blob. pPayload is |
| 136 | 155 | ** the complete payload (including the login card if pLogin is NULL or |
| @@ -141,22 +160,22 @@ | ||
| 141 | 160 | Blob *pHdr, /* construct the header here */ |
| 142 | 161 | Blob *pLogin, /* Login card header value or NULL */ |
| 143 | 162 | const char *zAltMimetype /* Alternative mimetype */ |
| 144 | 163 | ){ |
| 145 | 164 | int nPayload = pPayload ? blob_size(pPayload) : 0; |
| 146 | - const char *zPath; | |
| 147 | 165 | |
| 148 | 166 | blob_zero(pHdr); |
| 167 | + blob_appendf(pHdr, "%s ", nPayload>0 ? "POST" : "GET"); | |
| 149 | 168 | if( g.url.subpath ){ |
| 150 | - zPath = g.url.subpath; | |
| 169 | + /* The subpath is already percent-encoded. */ | |
| 170 | + blob_append(pHdr, g.url.subpath, -1); | |
| 151 | 171 | }else if( g.url.path==0 || g.url.path[0]==0 ){ |
| 152 | - zPath = "/"; | |
| 172 | + blob_append_char(pHdr, '/'); | |
| 153 | 173 | }else{ |
| 154 | - zPath = g.url.path; | |
| 174 | + http_append_request_target(pHdr, g.url.path); | |
| 155 | 175 | } |
| 156 | - blob_appendf(pHdr, "%s %s HTTP/1.1\r\n", | |
| 157 | - nPayload>0 ? "POST" : "GET", zPath); | |
| 176 | + blob_appendf(pHdr, " HTTP/1.1\r\n"); | |
| 158 | 177 | if( g.url.proxyAuth ){ |
| 159 | 178 | blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth); |
| 160 | 179 | } |
| 161 | 180 | if( g.zHttpAuth && g.zHttpAuth[0] ){ |
| 162 | 181 | const char *zCredentials = g.zHttpAuth; |
| 163 | 182 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -127,10 +127,29 @@ | |
| 127 | blob_appendf(pLogin, "login %F %b %b", zLogin, &nonce, &sig); |
| 128 | blob_reset(&pw); |
| 129 | blob_reset(&sig); |
| 130 | blob_reset(&nonce); |
| 131 | } |
| 132 | |
| 133 | /* |
| 134 | ** Construct an appropriate HTTP request header. Write the header |
| 135 | ** into pHdr. This routine initializes the pHdr blob. pPayload is |
| 136 | ** the complete payload (including the login card if pLogin is NULL or |
| @@ -141,22 +160,22 @@ | |
| 141 | Blob *pHdr, /* construct the header here */ |
| 142 | Blob *pLogin, /* Login card header value or NULL */ |
| 143 | const char *zAltMimetype /* Alternative mimetype */ |
| 144 | ){ |
| 145 | int nPayload = pPayload ? blob_size(pPayload) : 0; |
| 146 | const char *zPath; |
| 147 | |
| 148 | blob_zero(pHdr); |
| 149 | if( g.url.subpath ){ |
| 150 | zPath = g.url.subpath; |
| 151 | }else if( g.url.path==0 || g.url.path[0]==0 ){ |
| 152 | zPath = "/"; |
| 153 | }else{ |
| 154 | zPath = g.url.path; |
| 155 | } |
| 156 | blob_appendf(pHdr, "%s %s HTTP/1.1\r\n", |
| 157 | nPayload>0 ? "POST" : "GET", zPath); |
| 158 | if( g.url.proxyAuth ){ |
| 159 | blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth); |
| 160 | } |
| 161 | if( g.zHttpAuth && g.zHttpAuth[0] ){ |
| 162 | const char *zCredentials = g.zHttpAuth; |
| 163 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -127,10 +127,29 @@ | |
| 127 | blob_appendf(pLogin, "login %F %b %b", zLogin, &nonce, &sig); |
| 128 | blob_reset(&pw); |
| 129 | blob_reset(&sig); |
| 130 | blob_reset(&nonce); |
| 131 | } |
| 132 | |
| 133 | /* |
| 134 | ** Append zPath to pBlob as the target of an HTTP request line (RFC 9112 |
| 135 | ** section 3.2). zPath is the decoded path from the URL, so it is |
| 136 | ** percent-encoded again here: each byte outside ASCII and each |
| 137 | ** character that RFC 3986 section 3.3 does not allow in a path is |
| 138 | ** written as %XX. '+' is encoded too, because the server decodes it |
| 139 | ** as a space. |
| 140 | */ |
| 141 | static void http_append_request_target(Blob *pBlob, const char *zPath){ |
| 142 | for(; *zPath; zPath++){ |
| 143 | unsigned char c = (unsigned char)*zPath; |
| 144 | if( c<=0x20 || c>=0x7f || strchr("\"#%+<>?[\\]^`{|}", c)!=0 ){ |
| 145 | blob_appendf(pBlob, "%%%02X", c); |
| 146 | }else{ |
| 147 | blob_append_char(pBlob, (char)c); |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | /* |
| 153 | ** Construct an appropriate HTTP request header. Write the header |
| 154 | ** into pHdr. This routine initializes the pHdr blob. pPayload is |
| 155 | ** the complete payload (including the login card if pLogin is NULL or |
| @@ -141,22 +160,22 @@ | |
| 160 | Blob *pHdr, /* construct the header here */ |
| 161 | Blob *pLogin, /* Login card header value or NULL */ |
| 162 | const char *zAltMimetype /* Alternative mimetype */ |
| 163 | ){ |
| 164 | int nPayload = pPayload ? blob_size(pPayload) : 0; |
| 165 | |
| 166 | blob_zero(pHdr); |
| 167 | blob_appendf(pHdr, "%s ", nPayload>0 ? "POST" : "GET"); |
| 168 | if( g.url.subpath ){ |
| 169 | /* The subpath is already percent-encoded. */ |
| 170 | blob_append(pHdr, g.url.subpath, -1); |
| 171 | }else if( g.url.path==0 || g.url.path[0]==0 ){ |
| 172 | blob_append_char(pHdr, '/'); |
| 173 | }else{ |
| 174 | http_append_request_target(pHdr, g.url.path); |
| 175 | } |
| 176 | blob_appendf(pHdr, " HTTP/1.1\r\n"); |
| 177 | if( g.url.proxyAuth ){ |
| 178 | blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth); |
| 179 | } |
| 180 | if( g.zHttpAuth && g.zHttpAuth[0] ){ |
| 181 | const char *zCredentials = g.zHttpAuth; |
| 182 |